webpack 在编译代码过程中,会触发⼀系列 Tapable 钩⼦事件,插件所做的,就是找到相应的钩⼦,往上⾯挂上⾃⼰的任务,也就是注册事件,这样当 webpack 构建的时候,插件注册的事件就会随着钩⼦的触发⽽执⾏了。

plugin: 开始打包,在某个时刻,帮助我们处理⼀些什么事情的机制。

plugin 要⽐ loader 稍微复杂⼀些,在 webpack 的源码中,⽤ plugin 的机制还是占有⾮常⼤的场景,可以说plugin 是 webpack 的灵魂。

插件没有像 loader 那样的独立运行环境,只能在 webpack 里面运行

插件的基本结构

  1. class MyPlugin { // 插件名称
  2. apply(compiler) { // 插件上的 apply 方法
  3. compiler.hooks.done.tap(' My Plugin', ( // 插件的 hooks
  4. stats /* stats is passed as argument when done hook is tapped. */
  5. ) => {
  6. console.log('Hello World!'); // 插件处理逻辑
  7. });
  8. }
  9. }
  10. module.exports = MyPlugin;

插件使用:

  1. const path = require("path");
  2. const DemoPlugin = require("./plugins/demo-plugin.js");
  3. const PATHS = {
  4. lib: path.join(__dirname, "app", "shake.js"),
  5. build: path.join(__dirname, "build"),
  6. };
  7. module.exports = {
  8. entry: {
  9. lib: PATHS.lib,
  10. },
  11. output: {
  12. path: PATHS.build,
  13. filename: "[name].js",
  14. },
  15. plugins: [new DemoPlugin()],
  16. }

一个最简单的插件

src/demo-plugin.js

  1. module.exports = class DemoPlugin {
  2. constructor(options) {
  3. this.options = options; //获取传递的参数
  4. }
  5. apply() {
  6. console.log("apply", this.options);
  7. }
  8. }

加入到 webpack 配置中

  1. module.exports = {
  2. ...
  3. plugins: [new DemoPlugin({ name: "demo" })]
  4. };

插件的错误处理

  • 参数校验阶段可以直接 throw 的方式抛出throw new Error(“ Error Message”)
  • 通过 compilation 对象的 warnings 和 errors 接收

    1. compilation.warnings.push("warning");
    2. compilation.errors.push("error");

    通过 Compilation 进行文件写入

    Compilation 上的 assets 可以用于文件写入

  • 可以将 zip 资源包设置到 compilation.assets 对象上

文件写入需要使用 webpack-sources (https://www.npmjs.com/package/webpack-sources)

  1. const { RawSource } = require("webpack-sources");
  2. module.exports = class DemoPlugin {
  3. constructor(options) {
  4. this.options = options;
  5. }
  6. apply(compiler) {
  7. const { name } = this.options;
  8. compiler.plugin("emit", (compilation, cb) => {
  9. compilation.assets[name] = new RawSource("demo");
  10. cb();
  11. });
  12. }
  13. };

编写插件的插件

插件自身也可以通过暴露 hooks 的方式进行自身扩展,以 html-webpack-plugin 为例:

  • html-webpack-plugin-alter-chunks (Sync)
  • html-webpack-plugin-before-html-generation (Async)
  • html-webpack-plugin-alter-asset-tags (Async)
  • html-webpack-plugin-after-html-processing (Async)
  • html-webpack-plugin-after-emit (Async)

编写一个压缩构建资源为zip包的插件

  • 生成的 zip 包文件名称可以通过插件传入
  • 需要使用 compiler 对象上的特地 hooks 进行资源的生成

Node.js 里面将文件压缩为 zip 包,使用 jszip (https://www.npmjs.com/package/jszip))

  1. var zip = new JSZip();
  2. zip.file("Hello.txt", "Hello World\n");
  3. var img = zip.folder("images");
  4. img.file("smile.gif", imgData, {base64: true});
  5. zip.generateAsync({type:"blob"}).then(function(content) {
  6. // see FileSaver.js
  7. saveAs(content, "example.zip");
  8. });
  • 创建copyright-webpack-plugin.js

    1. class CopyrightWebpackPlugin {
    2. constructor() {
    3. }
    4. //compiler:webpack实例
    5. apply(compiler) {
    6. }
    7. }
    8. module.exports = CopyrightWebpackPlugin;
  • 配置⽂件⾥使⽤

    1. const CopyrightWebpackPlugin = require("./plugin/copyright-webpack-plugin");
    2. plugins: [new CopyrightWebpackPlugin()]
  • 如何传递参数 ```javascript //webpack配置⽂件 plugins: [ new CopyrightWebpackPlugin({ name: ‘开课吧’ }) ];

//copyright-webpack-plugin.js class CopyrightWebpackPlugin { constructor(options) { //接受参数 console.log(options); } apply(compiler) {} } module.exports = CopyrightWebpackPlugin;

  1. - **配置 plugin 在什么时刻进⾏**
  2. ```javascript
  3. class CopyrightWebpackPlugin {
  4. constructor(options) {
  5. // console.log(options);
  6. }
  7. apply(compiler) {
  8. //hooks.emit 定义在某个时刻
  9. compiler.hooks.emit.tapAsync(
  10. 'CopyrightWebpackPlugin',
  11. (compilation, cb) => {
  12. compilation.assets['copyright.txt'] = {
  13. source: function () {
  14. return 'hello copy';
  15. },
  16. size: function () {
  17. return 20;
  18. }
  19. };
  20. cb();
  21. }
  22. );
  23. //同步的写法
  24. //compiler.hooks.compile.tap("CopyrightWebpackPlugin", compilation => {
  25. // console.log("开始了");
  26. //});
  27. }
  28. }
  29. module.exports = CopyrightWebpackPlugin;

具体有哪些预先定义好的钩子,我们可以参考官方文档的 API: Compiler Hooks:https://webpack.js.org/api/compiler-hooks/ Compilation Hooks:https://webpack.js.org/api/compilation-hooks/ JavascriptParser Hooks:https://webpack.js.org/api/parser/