webpack实现常用plugin
在 webpack 中, plugin 主要做的事情围绕着整一个构建的过程。基于 tapable 的钩子机制,开发者可以在众多构建环节中注册相关的事件,依托于 webpack 提供的构建上下文,来对打包结果进行一些处理
plugin简要介绍
每一个 plugin 都是一个类,主要关注这个类的两个方法,一个是构造函数 constructor ,还有一个是 apply 方法。在 constructor 中可以获得配置 plugin 时传入的参数,而 apply 方法则是 webpack 会调用的方法。每个插件都有两个重要的钩子,一个是compiler钩子,还有一个是compilation钩子。
complier 是 webpack 构建启动时产生的,只有一个,它可以访问构建的各种配置等等。 compilation 是对资源的一次构建,可以有多个,它可以访问构建过程中的资源。下面以 complier 为例,介绍钩子事件时如何注册的。
从时序的角度来看, complier 钩子有三种类型,分别是同步钩子、异步钩子、异步 promise 钩子
代码示例:
// 同步插件
class aosPlugin {apply(compiler) {compiler.hooks.xxx.tap('aosPlugin',()=>{console.log('aosPlugin')})}
}
//异步插件
class aosPlugin2 {apply(compiler) {compiler.hooks.xxx.tap('aosPlugin2', (complication, callback) => {console.log('aosPlugin2')callback()})}
}
实现一个插件
这个插件主要做下面几件事情:
• 注册 emit 钩子
• 获取即将输出的资源,筛选出 html 文件
• 把注释内容添加到文件中
• 输出资源
开发一个 plugin ,首先要想清楚的问题是,这个插件是在什么时候执行的。在我学习以及开发的过程中, emit (资源输出前)和 afterEmit (资源输出后)这两个钩子是最经常用到的。当然我的意思不是这两个钩子就是开发 plugin 最常用到的两个钩子,而是想说我们想开发一个 plugin ,必须先想好执行的时机,选择好注册事件的钩子
这个钩子要做的是,对于我们输出的资源,希望打上一些注释。我们上次在 loader 也做过同样的事情,但是 loader 处理过后的资源可能会被合并、压缩。对于注释可能会被删掉
代码示例:
const path = require("path");
const os = require("os");function getLocalIP() {const interfaces = os.networkInterfaces();for (const interfaceName in interfaces) {let interfacess = interfaces[interfaceName];for (let i = 0; i < interfacess.length; i++) {const { address, family, internal } = interfacess[i];// 过滤出IPv4地址,并排除内部地址if (family === "IPv4" && !internal) {return address;}}}return null; // 无法获取IP时返回null
}class MyPlugin {apply(compiler) {compiler.hooks.emit.tap("MyPlugin", (compilation) => {const { assets } = compilation;const files = Object.keys(assets).filter((filename) => {const exts = ["html"];const arr = filename.split(".");const fileExt = arr[arr.length - 1];return exts.includes(fileExt);});const prefix = `<!--date: ${new Date().toLocaleString()}IP: ${getLocalIP() || "unknown"}程序出Bug了?∩∩(´・ω・)_| ⊃/(___/ └-(___/ ̄ ̄ ̄ ̄ ̄ ̄ ̄算了反正不是我写的⊂⌒/ヽ-、_/⊂_/____ / ̄ ̄ ̄ ̄ ̄ ̄ ̄-->`;files.forEach((file) => {const source = assets[file].source();const newContent = prefix + source;assets[file] = {source() {return newContent;},size() {return newContent.length;},};});});}
}module.exports = MyPlugin;
使用plugin
vue.config.js文件中添加plugin
const MyPlugin = require("./plugin");module.exports = {...configureWebpack: {plugins: [new MyPlugin(),],},
};
webpack 输出阶段会发生的事件钩子
should-emit 所有需要输出的文件已经生成,询问插件有哪些文件需要输出,有哪些不需要输出
emit 确定好要输出哪些文件后,执行文件输出,可以在这里获取和修改输出的内容
after-emit 文件输出完毕
done 成功完成一次完整的编译和输出流程
failed 如果在编译和输出的流程遇到异常,导致 webpack 退出,就会直接跳转到本步骤,插件可以在本事件中获取具体错误原因