1. plugin #

插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。创建插件比创建 loader 更加高级,因为你将需要理解一些 webpack 底层的内部特性来做相应的钩子

1.1 为什么需要一个插件 #

1.2 可以加载插件的常用对象 #

对象 钩子
Compiler run,compile,compilation,make,emit,done
Compilation buildModule,normalModuleLoader,succeedModule,finishModules,seal,optimize,after-seal
Module Factory beforeResolver,afterResolver,module,parser
Module
Parser] program,statement,call,expression
Template hash,bootstrap,localVars,render

2. 创建插件 #

webpack 插件由以下组成:

3. Compiler 和 Compilation #

在插件开发中最重要的两个资源就是compilercompilation对象。理解它们的角色是扩展webpack引擎重要的第一步。

4. 基本插件架构 #

4.1 使用插件代码 #

if (options.plugins && Array.isArray(options.plugins)) {
    for (const plugin of options.plugins) {
        plugin.apply(compiler);
    }
}

4.2 Compiler插件 #

4.2.1 同步 #

class DonePlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.done.tap('DonePlugin', (stats) => {
            console.log('Hello ', this.options.name);
        });
    }
}
module.exports = DonePlugin;

4.2.2 异步 #

class DonePlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.done.tapAsync('DonePlugin', (stats, callback) => {
            console.log('Hello ', this.options.name);
            callback();
        });
    }
}
module.exports = DonePlugin;

4.3 使用插件 #

const DonePlugin=require('./plugins/DonePlugin');
module.exports={
    entry: './src/index.js',
    output: {
        path: path.resolve('build'),
        filename:'bundle.js'
    },
    plugins: [
        new DonePlugin({name:'zfpx'})
    ]
}

4.4 触发钩子执行 #

if (this.hooks.shouldEmit.call(compilation) === false) {
                const stats = new Stats(compilation);
                stats.startTime = startTime;
                stats.endTime = Date.now();
+                this.hooks.done.callAsync(stats, err => {
+                    if (err) return finalCallback(err);
+                    return finalCallback(null, stats);
+                });
                return;
            }

5. compilation插件 #

5.1 asset-plugin.js #

plugins\asset-plugin.js

class AssetPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.compilation.tap('AssetPlugin', function (compilation) {
            compilation.hooks.chunkAsset.tap('AssetPlugin', function (chunk, filename) {
                console.log('filename=', filename);
            });
        });
    }
}
module.exports = AssetPlugin;

5.2 compilation.call #

newCompilation(params) {
        const compilation = this.createCompilation();
        this.hooks.compilation.call(compilation, params);
        return compilation;
    }

5.3 chunkAsset.call #

chunk.files.push(file);
+this.hooks.chunkAsset.call(chunk, file);

关于 compiler, compilation 的可用回调,和其它重要的对象的更多信息,请查看 插件 文档。

6. 打包zip #

const { RawSource } = require("webpack-sources");
const JSZip = require("jszip");
const path = require("path");
class ZipPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        let that = this;
        compiler.hooks.emit.tapAsync("ZipPlugin", (compilation, callback) => {
            var zip = new JSZip();
            for (let filename in compilation.assets) {
                const source = compilation.assets[filename].source();
                zip.file(filename, source);
            }
            zip.generateAsync({ type: "nodebuffer" }).then(content => {
                compilation.assets[that.options.filename] = new RawSource(content);
                callback();
            });
        });
    }
}
module.exports = ZipPlugin;

6.2 webpack.config.js #

webpack.config.js

  plugins: [
+    new zipPlugin({
+      filename: 'assets.zip'
+    })
]

7.自动外链 #

7.1 使用外部类库 #

能否检测代码中的import自动处理这个步骤?

7.2 思路 #

7.3 使用plugins #

plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename:'index.html'
        }),
        new AutoExternalPlugin({
            jquery: {
                expose: '$',
                url: 'https://cdn.bootcss.com/jquery/3.1.0/jquery.js'
            }
        })
    ]

7.4 AutoExternalPlugin #

const ExternalModule = require('webpack/lib/ExternalModule');
class AutoExternalPlugin {
    constructor(options) {
        this.options = options;
        this.externalModules = {};
    }
    apply(compiler) {
        //1.在解析语法树的过程中查找那些需要外部引入的模块名称
        compiler.hooks.normalModuleFactory.tap('AutoExternalPlugin', normalModuleFactory => {
            normalModuleFactory.hooks.parser
                .for('javascript/auto')
                .tap('AutoExternalPlugin', parser => {
                    parser.hooks.import.tap('AutoExternalPlugin', (statement, source) => {
                        if (this.options[source])
                            this.externalModules[source] = true;
                    });
                });
            //2.在生产模块的过程中发现如果是外部模块则返回外部模块
            normalModuleFactory.hooks.factory.tap('AutoExternalPlugin', factory => (data, callback) => {
                const dependency = data.dependencies[0];
                let value = dependency.request;//jquery
                if (this.externalModules[value]) {
                    let varName = this.options[value].expose;
                    callback(null, new ExternalModule(varName, 'window'));
                } else {
                    factory(data, callback);
                }
            });
        });
        compiler.hooks.compilation.tap('AutoExternalPlugin', compilation => {
            //3.向body底部插入全局变量的脚本
            compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync('normalModuleFactory', (htmlPluginData, callback) => {
                Object.keys(this.externalModules).forEach(name => {
                    let src = this.options[name].url;
                    htmlPluginData.body.unshift({
                        tagName: 'script',
                        closeTag: true,
                        attributes: { type: 'text/javascript', src }
                    });
                });
                callback(null, htmlPluginData);
            });
        });
    }
}
module.exports = AutoExternalPlugin;

8. 参考 #

webpackcode