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 退出,就会直接跳转到本步骤,插件可以在本事件中获取具体错误原因

相关推荐
golang学习记3 小时前
从0死磕全栈之Next.js 字体优化实战:零布局偏移、高性能、隐私友好的字体加载方案
前端
zachhere4 小时前
深入了解 OpenAI Apps SDK 的内部机制
前端
张可爱4 小时前
20251015-Vue3八股文整理
前端
ruanCat4 小时前
记一次因 vue-router 升级而导致的 uniapp 故障
前端·vue.js
Damon小智4 小时前
基于 Rokid JSAR 打造精致的 3D 白色飞机模型
前端·虚拟现实
Mintopia4 小时前
🌌 知识图谱与 AIGC 融合:
前端·javascript·aigc
三十_4 小时前
TypeORM 基础篇:项目初始化与增删改查全流程
前端·后端
小时前端4 小时前
事件委托性能真相:90%内存节省背后的数据实证
前端·dom