美文网首页
如何编写自定义webpack plugin?

如何编写自定义webpack plugin?

作者: 努力学习的小丸子 | 来源:发表于2021-05-14 16:37 被阅读0次

    参考文章:https://webpack.docschina.org/api/compiler-hooks/
    webpack插件包括:

    • 具名function或class
    • 原型上有apply方法
    • 在特定钩子函数执行代码
    • 处理compiler或compilation对象上的数据
    • 调用webpack提供的回调
    // A JavaScript class.
    class MyExampleWebpackPlugin {
      // Define `apply` as its prototype method which is supplied with compiler as its argument
      apply(compiler) {
        // Specify the event hook to attach to
        compiler.hooks.emit.tapAsync(
          'MyExampleWebpackPlugin',
          (compilation, callback) => {
            console.log('This is an example plugin!');
            console.log(
              'Here’s the `compilation` object which represents a single build of assets:',
              compilation
            );
    
            // Manipulate the build using the plugin API provided by webpack
            compilation.addModule(/* ... */);
    
            callback();
          }
        );
      }
    }
    
    apply(compiler) {
            let skeletons;
            // compatible with webpack 4.x
            if (compiler.hooks) {
                compiler.hooks.make.tapAsync(PLUGIN_NAME, (compilation, cb) => {
                    if (!compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing) {
                        console.error('VueSkeletonWebpackPlugin must be placed after HtmlWebpackPlugin in `plugins`.');
                        return;
                    }
    
                    this.generateSkeletonForEntries(this.extractEntries(compiler.options.entry), compiler, compilation)
                        .then(skeletonResults => {
                            skeletons = skeletonResults.reduce((cur, prev) => Object.assign(prev, cur), {});
                            cb();
                        })
                        .catch(e => console.log(e));
    
                    compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tapAsync(PLUGIN_NAME, (htmlPluginData, callback) => {
                        this.injectToHtml(htmlPluginData, skeletons);
                        callback(null, htmlPluginData);
                    });
                });
            }
            else {
                compiler.plugin('make', (compilation, cb) => {
                    this.generateSkeletonForEntries(this.extractEntries(compiler.options.entry), compiler, compilation)
                        .then(skeletonResults => {
                            skeletons = skeletonResults.reduce((cur, prev) => Object.assign(prev, cur), {});
                            cb();
                        })
                        .catch(e => console.log(e));
                    
                    compilation.plugin('html-webpack-plugin-before-html-processing', (htmlPluginData, callback) => {
                        this.injectToHtml(htmlPluginData, skeletons);
                        callback(null, htmlPluginData);
                    });
                });
            }
        }
    

    compiler所有钩子函数详见:https://webpack.docschina.org/api/compiler-hooks/
    compiler钩子函数使用示例:
    emit : 生成资源到output之前,在需要更改输出的文件时很有用,比如去掉所有的console.log。
    make :compilation 结束之前执行。
    compilation :compilation 创建之后执行。
    afterPlugins :在初始化内部插件集合完成设置之后调用。

    对于初始化插件需传参的情况,在constructor中定义。以下使用了schema-utils来校验传入的参数是否符合规范。

    import { validate } from 'schema-utils';
    import schema from 'path/to/schema.json';
    class Plugin {
      constructor(options) {
        validate(schema, options, {
          name: 'Plugin Name',
          baseDataPath: 'options',
        });
        this.options = options;
      }
      apply(compiler) {
        // Code...
      }
    }
    export default Plugin;
    
    

    插件调用顺序

    插件按照在配置文件中的定义顺序依次执行,并且插件必须调用钩子函数中的cb方法才会执行下一个插件。
    比如,自动生成一些js文件。在生成文件之后再执行GenerateRenderIndexPlugin插件。

    // generateRenderApiPlugin.js
    class GenerateRenderApiPlugin {
        apply(compiler) {
            compiler.hooks.beforeRun.tapAsync('GenerateRenderApiPlugin', (compilation, cb) => {
                // 该插件中需要执行一些异步操作
                let actions = [Promise.resolve()]
                Promise.all(actions).then(() => {
                    cb();
                }).catch((e) => {
                    // cb()
                })
            })
    
        }
    }
    
    //  webpack.config.js
     plugins: [
            new GenerateRenderApiPlugin(),
            new GenerateRenderIndexPlugin()
    ]
    

    将字符串变成可执行代码:

    vm.runInThisContext(`let a = 1`)
    console.log(a);
    

    输出格式化的代码到文件:

    const beautify = require('js-beautify').js;
    util.promisify(fs.writeFile)(`${outputPath}/${prefix}.js`, beautify(apiContent, { indent_size: 2, space_in_empty_paren: true }), 'utf8').then((data) => {
    
                        console.log('test write success')
                    })
    

    字符串全部替换:

    const replaceAll = (target, reg, slot) => {
        return target.replace(reg, slot);
    }
    let reg = new RegExp(/customR/g);
    replaceAll(`let {a} = customR('./a.js')`, reg, 'require');
    

    插件传参

    const { validate } = require('schema-utils')
    const schema = {
        type: "object",
        properties: {
            test: {
                type: "string",
            },
        },
    };
    class RemoveConsolePlugin {
        constructor(options) {
            console.log('init plugin');
            validate(schema, options, {
                name: 'Plugin Name',
                baseDataPath: 'options',
            });
            this.options = options;
        }
        apply(compiler) {
            if (compiler.hooks) {
                console.log('webpack 4');
            } else {
                console.log('webpack 5');
            }
        }
    }
    module.exports = RemoveConsolePlugin;
    

    相关文章

      网友评论

          本文标题:如何编写自定义webpack plugin?

          本文链接:https://www.haomeiwen.com/subject/wjjblltx.html