webpack实现常用plugin

在 webpack 中, plugin 主要做的事情围绕着整一个构建的过程。基于 tapable 的钩子机制,开发者可以在众多构建环节中注册相关的事件,依托于 webpack 提供的构建上下文,来对打包结果进行一些处理

plugin简要介绍

每一个 plugin 都是一个类,主要关注这个类的两个方法,一个是构造函数 constructor ,还有一个是 apply 方法。在 constructor 中可以获得配置 plugin 时传入的参数,而 apply 方法则是 webpack 会调用的方法。每个插件都有两个重要的钩子,一个是compiler钩子,还有一个是compilation钩子。
complier 是 webpack 构建启动时产生的,只有一个,它可以访问构建的各种配置等等。complier hooks有三种类型,分别是同步钩子、异步钩子、异步 promise 钩子。
compilation 是对资源的一次构建,可以有多个,它可以访问构建过程中的资源。

compilation主要有以下属性:

modules 可以访问所有模块,打包的每一个文件都是一个模块。

chunks chunk 即是多个 modules 组成而来的一个代码块。入口文件引入的资源组成一个 chunk,通过代码分割的模块又是另外的 chunk。

assets 可以访问本次打包生成所有文件的结果。

hooks 可以注册 tapable 的不同种类 Hook,用于在 compilation 编译模块阶段进行逻辑添加以及修改。

下面以 complier 为例,介绍钩子事件时如何注册的。

代码示例:

js 复制代码
// 同步插件
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 处理过后的资源可能会被合并、压缩。对于注释可能会被删掉

代码示例:

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

js 复制代码
const MyPlugin = require("./plugin");

module.exports = {
	...
  configureWebpack: {
    plugins: [
      new MyPlugin(),
    ],
  },
};

webpack 输出阶段会发生的事件钩子

should-emit 所有需要输出的文件已经生成,询问插件有哪些文件需要输出,有哪些不需要输出

emit 确定好要输出哪些文件后,执行文件输出,可以在这里获取和修改输出的内容

after-emit 文件输出完毕

done 成功完成一次完整的编译和输出流程

failed 如果在编译和输出的流程遇到异常,导致 webpack 退出,就会直接跳转到本步骤,插件可以在本事件中获取具体错误原因

相关推荐
bearpping3 小时前
Nginx 配置:alias 和 root 的区别
前端·javascript·nginx
@大迁世界3 小时前
07.React 中的 createRoot 方法是什么?它具体如何运作?
前端·javascript·react.js·前端框架·ecmascript
January12073 小时前
VBen Admin Select 选择框选中后仍然显示校验错误提示的解决方案
前端·vben
. . . . .3 小时前
前端测试框架:Vitest
前端
xiaotao1314 小时前
什么是 Tailwind CSS
前端·css·css3
战南诚5 小时前
VUE中,keep-alive组件与钩子函数的生命周期
前端·vue.js
发现一只大呆瓜5 小时前
React-彻底搞懂 Redux:从单向数据流到 useReducer 的终极抉择
前端·react.js·面试
霍理迪5 小时前
Vue的响应式和生命周期
前端·javascript·vue.js
李剑一5 小时前
别再瞎写了!Cesium 模型 360° 环绕,4 套源码全公开,项目直接用
前端