记录一个webpack插件的书写过程

798 阅读1分钟

简介

最近在做一个公共ui组件库的开发工作,针对公共库,有一定的规范,比如需要一个压缩版本,一个未压缩版本,一个索引文件。一般生成后的目录如下:

  • xxx.js
  • xxx.min.js
  • index.js
// 索引文件的内容
if (process.env.NODE_ENV == "production") {
   module.exports = require("./xxx.min.js");
} else {
  module.exports = require("./xxx.js");
}

生成压缩和未压缩版本,我们可以配置两个entry,然后使用terser-webpack-plugin,指定文件压缩。webpack的大概配置文件如下:

module.exports = {
 entry: {
      'button': 'src/button/index.js',
      'button.min': 'src/button/index.js'
  },
  mode: 'none',
  output: {
      path: './dist',
      filename: '[name].js',
      library: 'xxx',
      libraryTarget: 'umd'
  },
  optimization: {
      minimize: true,
      minimizer: [
          new TerserPlugin(
              {test: /\.min\.js$/}
          ),
      ]
  }
}

当有多个组件时, 遇到的问题:

  • 文件目录的组织形式,不符合要求。 当前的:

期望的:

  • 需要添加一个索引文件,根据环境,动态的导入不同的版本。

插件设计

  • 做目录合并,将button/button.js和button.min/button.min.js, 合并成button文件夹。
button
	button.js
	button.min.js

可以使用emit hooks, 在资源输出之前,是最佳的时机。

  • 为每一个组件,添加一个索引文件。

编写插件

// 插件代码
class FoldingDirWebpackPlugin {
    constructor(options = {}) {
        this.options = options;

        // 插件处理完成的回调。
        this.cb = options.cb;

        // 文件夹折叠的规则。
        this.exp = options.exp;
    }
    
    apply(compiler) {
      // 在emit阶段插入钩子函数
      compiler.hooks.emit.tap('FoldingDirWebpackPlugin', (compilation) => {
        const {assets} = compilation;
        let newAssets = {};

        for (const key in assets) {
            let newKey = key;

            if(this.exp){
                if(typeof this.exp === 'string'){
                    newKey = key.replace(this.exp, '');
                }else if(typeof this.exp === 'function'){
                    newKey = this.exp(key);
                }
            }
            newAssets[newKey] = assets[key];
        }

        compilation.assets = newAssets;

        if(this.cb){
            this.cb(compilation);
        }
      });
    }
  }
  
  module.exports = FoldingDirWebpackPlugin;

插件使用

const FoldingDirWebpackPlugin = require('folding-dir-webpack-plugin')

const generateIndex = compilation => {
    const {assets} = compilation;

    for (const key in assets) {
        if (Object.hasOwnProperty.call(assets, key)) {
            const arr = key.split('/');
            if (arr.length === 2) {
                let fileName = arr[0];
                const content = `if (process.env.NODE_ENV == "production") {module.exports = require("./${fileName}.min.js");} else {module.exports = require("./${fileName}.js");}`;

                compilation.assets[`${fileName}/index.js`] = {
                    source() {
                        return content;
                    },
                    size() {
                        return content.length;
                    }
                };
            }
        }
    }
}

module.exports = {
	// ...
    plugins: [
            new FoldingDirWebpackPlugin(
                {
                    exp: key => key.replace('.min/', '/'),
                    cb: generateIndex
                }
            )
        ],
};

插件安装

npm install -D folding-dir-webpack-plugin