这里用到 emit 钩子 及make 钩子,前者是串行后者是并行
- /**
- * 1.webpack加载webpack.config.js中所有配置,此时就会new TestPlugin(),执行插件的constructor
- 2.webpack创建compiler对象
- 3.遍历所有plugins中插件,调用插件的apply方法
- 4.执行剩下编译流程《触发各个hooks事件)
- */
-
- class TestPlugin {
- constructor() {
- console.log('testPlugin-constructor');
- }
-
- apply(compiler) {
- console.log('testPlugin-apply');
- // 由文档可知,environment是同步钩子,所以需要使用tap注册
- compiler.hooks.environment.tap("TestPlugin",() => (console.log("TestPlugin environment")))
-
- // 由文档可知,emit是异步串行钩子 AsyncSeriesHook
- // 串行则顺讯执行
- compiler.hooks.emit.tap("TestPlugin", (compilation) => {
- console.log("TestPlugin emit 111");
- })
- compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) =>{
- setTimeout(() => {
- console.log("Testplugin emit 222");
- callback();
- }, 2000)
- })
-
- compiler.hooks.emit.tapPromise("TestPlugin", (compilation) => {
- return new Promise((resolve) => {
- setTimeout(() => {
- console.log("TestPlugin emit 333"); resolve();
- },1000);
- })
- })
-
- // 由文档可知,make是异步并行钩子 AsyncParallelHook
- compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {
- compilation.hooks.seal.tap("TetsPlugin", ()=>{
- console.log("TestPlugin seal");
- })
-
-
- setTimeout(() => {
- console.log("Testplugin make 111");
- callback();
- }, 3000);
- })
-
- compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {
- setTimeout(() => {
- console.log("Testplugin make 222");
- callback();
- }, 1000);
- })
-
- compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {
- setTimeout(() => {
- console.log("Testplugin make 333");
- callback();
- }, 2000);
- })
- }
- }
- module.exports = TestPlugin
webpack.config.js中的配置
- // 引入插件
- const TestPlugin = require('./plugins/test-plugin')
-
-
-
- // 使用插件
- new TestPlugin()
打印结果

- class BannerWebpackPlugin {
- constructor(options = {}) {
- this.options = options;
- }
-
- apply(compiler) {
- // 在资源输出之前触发
- compiler.hooks.emit.tap("BannerWebpackPlugin", (compilation) => {
- // debugger;
- const extensions = ["css", "js"];
- // 1. 获取即将输出的资源文件:compilation.assets
- // 2. 过滤只保留js和css资源
- const assets = Object.keys(compilation.assets).filter((assetPath) => {
- // 将文件名切割 ['xxxx', 'js'] ['xxxx', 'css']
- const splitted = assetPath.split(".");
- // 获取最后一个文件扩展名
- const extension = splitted[splitted.length - 1];
- // 判断是否保护
- return extensions.includes(extension);
- });
-
- const prefix = `/*
- * Author: ${this.options.author}
- */
- `;
- // 3. 遍历剩下资源添加上注释
- // console.log(assets);
- assets.forEach((asset) => {
- // 获取原来内容
- const source = compilation.assets[asset].source();
- // 拼接上注释
- const content = prefix + source;
-
- // 修改资源
- compilation.assets[asset] = {
- // 最终资源输出时,调用source方法,source方法的返回值就是资源的具体内容
- source() {
- return content;
- },
- // 资源大小
- size() {
- return content.length;
- },
- };
- });
- });
- }
- }
-
- module.exports = BannerWebpackPlugin;
- class CleanWebpackPlugin {
- apply(compiler) {
- // 2. 获取打包输出的目录
- const outputPath = compiler.options.output.path;
- const fs = compiler.outputFileSystem;
- // 1. 注册钩子:在打包输出之前 emit
- compiler.hooks.emit.tap("CleanWebpackPlugin", (compilation) => {
- // 3. 通过fs删除打包输出的目录下的所有文件
- this.removeFiles(fs, outputPath);
- });
- }
-
- removeFiles(fs, filepath) {
- // 想要删除打包输出目录下所有资源,需要先将目录下的资源删除,才能删除这个目录
- // 1. 读取当前目录下所有资源
- const files = fs.readdirSync(filepath);
- // console.log(files); // [ 'images', 'index.html', 'js' ]
- // 2. 遍历一个个删除
- files.forEach((file) => {
- // 2.1 遍历所有文件,判断是文件夹还是文件
- const path = `${filepath}/${file}`;
- const fileStat = fs.statSync(path);
- // console.log(fileStat);
- if (fileStat.isDirectory()) {
- // 2.2 是文件夹,就得删除下面所有文件,才能删除文件夹
- this.removeFiles(fs, path);
- } else {
- // 2.3 是文件,直接删除
- fs.unlinkSync(path);
- }
- });
- }
- }
-
- module.exports = CleanWebpackPlugin;
- class AnalyzeWebpackPlugin {
- apply(compiler) {
- compiler.hooks.emit.tap("AnalyzeWebpackPlugin", (compilation) => {
- // 1. 遍历所有即将输出文件,得到其大小
- /*
- 将对象变成一个二维数组:
- 对象:
- {
- key1: value1,
- key2: value2
- }
- 二维数组:
- [
- [key1, value1],
- [key2, value2]
- ]
- */
- const assets = Object.entries(compilation.assets);
-
- /*
- md中表格语法:
- | 资源名称 | 资源大小 |
- | --- | --- |
- | xxx.js | 10kb |
- */
- let content = `| 资源名称 | 资源大小 |
- | --- | --- |`;
-
- assets.forEach(([filename, file]) => {
- content += `\n| ${filename} | ${Math.ceil(file.size() / 1024)}kb |`;
- });
-
- // 2. 生成一个md文件
- compilation.assets["analyze.md"] = {
- source() {
- return content;
- },
- size() {
- return content.length;
- },
- };
- });
- }
- }
-
- module.exports = AnalyzeWebpackPlugin;
生成md文件
让 小的js 文件直接内联到 html中
- const HtmlWebpackPlugin = require("safe-require")("html-webpack-plugin");
-
- class InlineChunkWebpackPlugin {
- constructor(tests) {
- this.tests = tests;
- }
-
- apply(compiler) {
- compiler.hooks.compilation.tap("InlineChunkWebpackPlugin", (compilation) => {
- // 1. 获取html-webpack-plugin的hooks
- const hooks = HtmlWebpackPlugin.getHooks(compilation);
- // 2. 注册 html-webpack-plugin的hooks -> alterAssetTagGroups
- hooks.alterAssetTagGroups.tap("InlineChunkWebpackPlugin", (assets) => {
- // 3. 从里面将script的runtime文件,变成inline script
- assets.headTags = this.getInlineChunk(assets.headTags, compilation.assets);
- assets.bodyTags = this.getInlineChunk(assets.bodyTags, compilation.assets);
- });
-
- // 删除runtime文件
- hooks.afterEmit.tap("InlineChunkWebpackPlugin", () => {
- // 3. 从里面将script的runtime文件,变成inline script
- Object.keys(compilation.assets).forEach((filepath) => {
- if (this.tests.some((test) => test.test(filepath))) {
- delete compilation.assets[filepath];
- }
- });
- });
- });
- }
-
- getInlineChunk(tags, assets) {
- /*
- 目前:[
- {
- tagName: 'script',
- voidTag: false,
- meta: { plugin: 'html-webpack-plugin' },
- attributes: { defer: true, type: undefined, src: 'js/runtime~main.js.js' }
- },
- ]
- 修改为:
- [
- {
- tagName: 'script',
- innerHTML: runtime文件的内容
- closeTag: true
- },
- ]
- */
-
- return tags.map((tag) => {
- if (tag.tagName !== "script") return tag;
- // 获取文件资源路径
- const filepath = tag.attributes.src;
- if (!filepath) return tag;
-
- if (!this.tests.some((test) => test.test(filepath))) return tag;
-
- return {
- tagName: "script",
- innerHTML: assets[filepath].source(),
- closeTag: true,
- };
- });
- }
- }
-
- module.exports = InlineChunkWebpackPlugin;