1. plugin
插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。创建插件比创建 loader 更加高级,因为你将需要理解一些 webpack 底层的内部特性来做相应的钩子
1.1 为什么需要一个插件
- webpack基础配置无法满足需求
- 插件几乎能够任意更改webpack编译结果
- webpack内部也是通过大量内部插件实现的
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 插件由以下组成:
- 一个 JavaScript 命名函数。
- 在插件函数的 prototype 上定义一个 apply 方法。
- 指定一个绑定到 webpack 自身的事件钩子。
- 处理 webpack 内部实例的特定数据。
- 功能完成后调用 webpack 提供的回调。
3. Compiler 和 Compilation
在插件开发中最重要的两个资源就是compiler和compilation对象。理解它们的角色是扩展webpack引擎重要的第一步。
- compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境
- compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
4. 基本插件架构
- 插件是由「具有 apply 方法的 prototype 对象」所实例化出来的
- 这个 apply 方法在安装插件时,会被 webpack compiler 调用一次
- apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象
4.1 使用插件源代码
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
plugin.apply(compiler);
}
}
4.2 调用Compiler插件done钩子创建插件
(1)同步插件使用tap
class DonePlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.done.tap('DonePlugin', (stats) => {
console.log('Hello ', this.options.name);
});
}
}
module.exports = DonePlugin;
(2)异步插件使用tapAsync
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 使用插件
- 要安装这个插件,只需要在你的 webpack 配置的 plugin 数组中添加一个实例
const DonePlugin=require('./plugins/DonePlugin');
module.exports={
entry: './src/index.js',
output: {
path: path.resolve('build'),
filename:'bundle.js'
},
plugins: [
new DonePlugin({name:'zfpx'})
]
}
最后在项目中实执行所有引用的plugin也都是在触发done钩子函数中执行的 源码地址
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;
}