webpack》》Plugin 原理
plugin
Plugin 原理
webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。 插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack 通过 Tapable 来组织这条复杂的生产线。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。 webpack 的事件流机制保证了插件的有序性,使得整个系统扩展性很好。 ——「深入浅出 Webpack」
Tapable 还统一暴露了三个方法给插件,用于注入不同类型的自定义构建行为:
tap:可以注册同步钩子和异步钩子。
tapAsync:回调方式注册异步钩子。
tapPromise:Promise 方式注册异步钩子。
compiler hooks 文档
compilation hooks 文档
自定义 Plugin
Webpack 插件是一个具有 apply 方法的 JavaScript 对象,通过操作 webpack 构建过程中的各个生命周期钩子来扩展功能。下面详细介绍如何开发自定义插件。
class MyPlugin {// 必须的 apply 方法apply(compiler) {// 在某个钩子上挂载插件逻辑compiler.hooks.someHook.tap('MyPlugin', (params) => {console.log('MyPlugin 正在执行');});}
}module.exports = MyPlugin;
最简单的插件
//plugins/test-plugin.jsclass TestPlugin {constructor() {console.log("TestPlugin constructor()");}// 1. webpack读取配置时,new TestPlugin() ,会执行插件 constructor 方法// 2. webpack创建 compiler 对象// 3. webpack 遍历所有插件,调用插件的 apply 方法// 4.执行剩下的编译流程(触发各个hook事件)apply(compiler) {console.log("TestPlugin apply()");}
}
module.exports = TestPlugin;
注册 hook
class TestPlugin {constructor() {console.log("TestPlugin constructor()");}// 1. webpack读取配置时,new TestPlugin() ,会执行插件 constructor 方法// 2. webpack创建 compiler 对象// 3. 遍历所有插件,调用插件的 apply 方法apply(compiler) {console.log("TestPlugin apply()");// 从文档可知, compile hook 是 SyncHook, 也就是同步钩子, 只能用tap注册compiler.hooks.compile.tap("TestPlugin", (compilationParams) => {console.log("compiler.compile()");});// 从文档可知, make 是 AsyncParallelHook, 也就是异步并行钩子, 特点就是异步任务同时执行// 可以使用 tap、tapAsync、tapPromise 注册。// 如果使用tap注册的话,进行异步操作是不会等待异步操作执行完成的。compiler.hooks.make.tap("TestPlugin", (compilation) => {setTimeout(() => {console.log("compiler.make() 111");}, 2000);});// 使用tapAsync、tapPromise注册,进行异步操作会等异步操作做完再继续往下执行compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {setTimeout(() => {console.log("compiler.make() 222");// 必须调用callback();}, 1000);});compiler.hooks.make.tapPromise("TestPlugin", (compilation) => {console.log("compiler.make() 333");// 必须返回promisereturn new Promise((resolve) => {resolve();});});// 从文档可知, emit 是 AsyncSeriesHook, 也就是异步串行钩子,特点就是异步任务顺序执行compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) => {setTimeout(() => {console.log("compiler.emit() 111");callback();}, 3000);});compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) => {setTimeout(() => {console.log("compiler.emit() 222");callback();}, 2000);});compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) => {setTimeout(() => {console.log("compiler.emit() 333");callback();}, 1000);});}
}
module.exports = TestPlugin;