webpack plugin

1、基本写法及使用

这里用到 emit 钩子 及make 钩子,前者是串行后者是并行

javascript 复制代码
/**
 *  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()

打印结果

2、BannerPlugin

javascript 复制代码
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;

3、CleanWebpackPlugin

javascript 复制代码
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;

4、AnalyzeWebpackPlugin

javascript 复制代码
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文件

5、InlineChunkWebpackPlugin

让 小的js 文件直接内联到 html中

javascript 复制代码
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;
相关推荐
天蓝色的鱼鱼1 小时前
Turbopack vs Webpack vs Vite:前端构建工具三分天下,谁将胜出?
前端·webpack
西洼工作室1 天前
Vue CLI为何不显示webpack配置
前端·vue.js·webpack
富贵2号2 天前
从零开始理解 Webpack:构建现代前端工程的基石
webpack
Hashan4 天前
告别混乱开发!多页面前端工程化完整方案(Webpack 配置 + 热更新)
webpack
开心不就得了5 天前
构建工具webpack
前端·webpack·rust
鲸落落丶6 天前
webpack学习
前端·学习·webpack
闲蛋小超人笑嘻嘻6 天前
前端面试十四之webpack和vite有什么区别
前端·webpack·node.js
guslegend6 天前
Webpack5 第五节
webpack
海涛高软8 天前
qt使用opencv的imread读取图像为空
qt·opencv·webpack
行者..................8 天前
手动编译 OpenCV 4.1.0 源码,生成 ARM64 动态库 (.so),然后在 Petalinux 中打包使用。
前端·webpack·node.js