一、简介:
插件向第三方开发者提供了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 |
二、创建插件
webpack插件由以下组成:
- 一个JavaScript命名函数
- 在插件函数的prototype上定义一个apply方法
- 指定一个绑定到webpack自身的事件钩子
- 处理webpack内部实例的特定数据
- 功能完成后调用webpack提供的回调
三、Compiler和Compilation
在插件开发中最重要的两个资源就是compiler和compilation对象。理解它们的角色是扩展webpack引擎重要的第一步。
- compiler对象代表了完整的webpack环境配置。这个对象在启动webpack时被一次性建立,并配置好所有可操作的设置,包括options, loader和plugin。当在webpack环境中应用一个插件时,插件将收到此compiler对象的引用。可以使用它来访问webpack的主环境
- compilation对象代表了一次资源版本构建。当运行webpack开发环境中间件时,每当检测到一个文件变化,就会创建一个新的compilation,从而生成一组新的编译资源。一个compilation对象表现了当前的模块资源,编译生成资源,变化的文件,以及被跟踪依赖的状态信息。compilation对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用
四、Tapable
webpack通过tapable实现自定义事件的触发和处理
4.1 同步钩子
const { SyncHook } = require('tapable');
const hook = new SyncHook(['name']);
hook.tap('hello', (name) => {
console.log(`hello ${name}`);
});
hook.tap('hello again', (name) => {
console.log(`hello ${name}, again`);
});
hook.call('jack');
// hello jack
// hello jack, again
每个事件独立执行 互不影响 按照注册顺序依次执行
4.2 异步钩子
const { AsyncParallelHook } = require('tapable');
const hook = new AsyncParallelHook(['name']);
hook.tapAsync('hello', (name, cb) => {
setTimeout(() => {
console.log(`hello ${name}`)
cb()
}, 2000)
})
hook.tapPromise('hello again', (name) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`hello ${name}, again`);
resolve();
}, 1000);
});
})
hook.callAsync('qqq', (name) => {
console.log(name)
})
// hello qqq, again
// hello qqq
// done
callAsync 当所有异步事件执行完后在执行此回调
五、基本插件架构
- 插件是由【具有apply方法的prototype对象】所实例化出来的
- 这个apply方法在安装插件时,会被webpack compiler调用一次
- apply方法可以接收一个webpack compiler对象的引用,从而可以在回到函数中访问到compiler对象
5.1 使用插件的代码
if(options.plugin && Array.isArray(options.plugins)) {
for(const plugin of options.plugins) {
plugin.apply(compiler)
}
}
六、实现一个简单的插件
监测打包时间插件
①新建文件夹
npm init -y 生成package.json
npm init -y
安装webpack webpack-cli
npm i webpack webpack-cli
②新建webpack.config.js文件并初始化
const {resolve} = require('path')
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js', // 入口文件
output: {
filename: 'built.js', // 打包文件
path: resolve(__dirname,'build')
},
module: {
rules: [
]
},
plugins: [
],
}
③新建src文件夹, 并新建index.js作为入口文件
④新建plugins文件夹,并新建donePlugin.js
class DonePlugin {
constructor(options) {
this.options = options
}
// compiler创建后, 会挂载所有的钩子 new DonePlugin().apply(compiler)
apply(compiler) {
// 蝙译开始时间
console.time('time')
// done代表打包完成
compiler.hooks.done.tapAsync('DonePlugin', (stats, callback) => {
// 蝙译结束时间
console.timeEnd('time')
callback()
})
}
}
module.exports = DonePlugin
⑤在webpack.config.js中使用插件
...
plugins: [
new DonePlugin()
],
⑥配置package.json打包命令
{
"name": "webpack-lianxi",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
}
}
⑦执行npm run build
time就是本次打包的时间 这样一个监测打包时间的插件就做好了