【webpack構建優化】——編寫enhanced-resolve插件

背景

最近在用webpack-bundle-analyzer包分析構建產物的時候發現soundmanager2的體積比較大。因而我在node_modules裏面找了下soundmanager2的代碼,發現默認導出的文件是帶調試功能的未壓縮版本。若是能夠在最終構建包裏面使用soundmanager2-nodebug-jsmin.js版本的話體積會小不少。node

圖片

動手

基於以前的經驗,我知道webpack是用enhanced-resolve來解析模塊滴,在github上找到enhanced-resolve,沒找到啥有用的信息,搜索了一番無果以後只好去看enhanced-resolve源碼了,還好源碼很少,看起來不是很累。具體怎麼看代碼的過程就不在這逼逼了,下面直接放解決方案。webpack

使用

// webpack.config.js
module.exports={
    resolve: {
        plugins: [
          new ReplaceModuleResolvePlugin({
            sourceModule: 'soundmanager2',
            destModule: 'soundmanager2-nodebug-jsmin.js',
          }),
        ],
      },
};
複製代碼

插件源碼

// utils.ts
export function cached(fn: Function) {
  const cache = Object.create(null);
  return function(str: string) {
    const hit = cache[str];
    // eslint-disable-next-line no-return-assign
    return hit || (cache[str] = fn(str));
  };
}

const extensionPattern = /\.(\w+)(?:$|\?.*)/;
export const getExtension = cached(function(path: string) {
  const result = path.match(extensionPattern);
  if (result && result[1]) {
    return result[1];
  }
  return '';
});
複製代碼
// ReplaceModuleResolvePlugin.ts
import { getExtension } from '../utils';

export interface ReplaceModuleOptions {
  sourceModule: string;
  destModule: string;
}

function ensureExtension(path: string, extension: string) {
  if (getExtension(path) !== '') {
    return path;
  }
  return `${path}.${extension}`;
}

// 構建時替換module
export class ReplaceModuleResolvePlugin {
  options: ReplaceModuleOptions;

  constructor(options: ReplaceModuleOptions) {
    this.options = options;
  }

  apply(resolver) {
    const { sourceModule, destModule } = this.options;
    const sourceModuleRegExp = new RegExp(sourceModule);

    resolver.getHook('existing-file').tapAsync('ReplaceModuleResolvePlugin', (request, resolveContext, callback) => {
      const innerRequest: string = request.request || request.path;

      if (!sourceModuleRegExp.test(innerRequest)) {
        return callback();
      }

      const extension = getExtension(innerRequest);

      const obj = {};
      Object.keys(request).forEach(key => {
        const val = request[key];
        if (typeof val === 'string') {
          obj[key] = val.replace(ensureExtension(sourceModule, extension), ensureExtension(destModule, extension));
        } else {
          obj[key] = val;
        }
      });

      return resolver.doResolve('resolved', obj, `ReplaceModuleResolvePlugin 使用目標模塊${destModule}`, resolveContext, callback);
    });
  }
}
複製代碼
相關文章
相關標籤/搜索