手写一个webpack插件(plugin)

熟悉 vue 和 react 的小伙伴们都知道,在执行过程中会有各种生命周期钩子,其实webpack也不例外,在使用webpack的时候,我们有时候需要在 webpack 构建流程中引入自定义的行为,这个时候就可以在 hooks 钩子中添加自己的方法。

创建插件

webpack 加载 webpack.config.js 中所有配置,此时 webpack 创建 compiler 对象,遍历所有 plugins 中插件,调用插件的 apply 方法,执行剩下编译流程(触发各个 hooks 事件),具体使用什么钩子和钩子是同步还是异步,请移步compiler 钩子

  1. 创建一个 JavaScript 命名函数或 JavaScript 类
  2. 在插件函数的 prototype 上定义一个 apply 方法
  3. 绑定到 webpack 自身的事件钩子上
  4. 导出这个JavaScript 命名函数或 JavaScript 类
  5. 在 webpack.config.js 文件中引入并调用方法

自定义 banner-webpack-plugin 插件,该插件会在每一个打包后的 js 、css 文件第一行添加注释,先看效果图。

  • emit 钩子是输出 asset 到 output 目录之前执行
  • 获取即将输出的资源文件:compilation.assets
  • 遍历 assets,只处理js和css资源,其他文件不处理
  • 通过 content = entcompilation.assets[filename].source() 获取原来内容
  • 拼接上注释 content = prefix + content
  • 修改资源的 source 和 size
js 复制代码
// plugins/banner-webpack-plugin.js

class BannerWebpackPlugin {
  constructor(options = {}) {
    this.options = options;
  }

  apply(compiler) {
    // 在资源输出之前触发
    compiler.hooks.emit.tap("BannerWebpackPlugin", (compilation) => {
      const extensions = ["css", "js"];
      const prefix = `/*
          * Author: ${this.options.author}
          * Build Time: ${new Date()}
          */
          `;
      // 获取即将输出的资源文件:compilation.assets
      for (const filename in compilation.assets) {
        if (compilation.assets.hasOwnProperty(filename)) {
          // 将文件名进行切割
          const splitted = filename.split(".");
          // 获取文件扩展名
          const extension = splitted[splitted.length - 1];
          // 只处理js和css资源,其他文件不处理
          if (extensions.includes(extension)) {
            const asset = compilation.assets[filename];
            // 获取原来内容
            let content = asset.source();
            // 拼接上注释
            content = prefix + content;
            // 修改资源
            compilation.assets[filename] = {
              // 最终资源输出时,调用source方法,source方法的返回值就是资源的具体内容
              source: () => content,
              // 资源大小
              size: () => content.length,
            };
          }
        }
      }
    });
  }
}
module.exports = BannerWebpackPlugin;

调用 BannerWebpackPlugin

js 复制代码
// config/webpack.config.js

// 引入插件
const BannerWebpackPlugin = require('../plugins/banner-webpack-plugin');
module.exports = {
  plugins: [
    // 调用插件
    new BannerWebpackPlugin({
      author: "小小愿望",
    }),
  ],
};

自定义 take-time-webpack-plugin

自定义 take-time-webpack-plugin 插件,该插件输出 "webpack 构建正在启动!",打包完成后输出 webpack 构建已完成!总耗时 { time } ms,先看效果图。

js 复制代码
// plugins/take-time-webpack-plugin.js

// 一个命名的 Javascript 方法 或 JavaScript 类
class TakeTimeWebpackPlugin {
  time = 0;
  // 原型上需要定义 apply 的方法
  apply(compiler) {
    // 生命周期钩子函数,是由 compiler 暴露
    // 通过 compiler 获取 webpack 内部的钩子,获取 Webpack 打包过程中的各个阶段
    compiler.hooks.environment.tap("TakeTimeWebpackPlugin", (compilation) => {
      console.log("\x1B[36m", "webpack 构建正在启动!");
      this.time = new Date().getTime();
    });
    // 通过 compiler 获取 webpack 内部的钩子,获取 Webpack 打包过程中的各个阶段
    compiler.hooks.afterEmit.tapAsync("TakeTimeWebpackPlugin", (compilation, callback) => {
      const nowTime = new Date().getTime();
      this.time = nowTime - this.time;
      const str = `webpack 构建已完成!总耗时 ${this.time} ms`
      console.log("\x1B[32m", str);
      // 分为同步和异步的钩子,异步钩子在功能完成后,必须执行对应的回调
      callback();
    });
  }
}
module.exports = TakeTimeWebpackPlugin;

调用 TakeTimeWebpackPlugin

在 config/webpack.config.js 文件中引入并执行 TakeTimeWebpackPlugin

js 复制代码
// config/webpack.config.js

// 引入插件
const TakeTimeWebpackPlugin = require('../plugins/take-time-webpack-plugin');
module.exports = {
  plugins: [
    // 调用插件
    new TakeTimeWebpackPlugin(),
  ],
};
相关推荐
我命由我123451 小时前
微信小程序 - scroll-view 的一些要点(scroll-view 需要设置滚动方向、scroll-view 需要设置高度)
开发语言·前端·javascript·微信小程序·小程序·前端框架·js
BD_Marathon1 小时前
【JavaWeb】CSS浮动
前端·css
1024肥宅1 小时前
手写 Promise:深入理解 JavaScript 异步编程的核心
前端·javascript·promise
铅笔侠_小龙虾1 小时前
Vue 学习目录
前端·vue.js·学习
悟能不能悟1 小时前
vue的history和hash模式有什么不一样
前端·vue.js
晓庆的故事簿1 小时前
前端的浏览器地址传参
前端
*星星之火*2 小时前
【大白话 AI 答疑】第6篇 大模型指令微调:instruction/input/output核心解析及案例
服务器·前端·人工智能
北杳同学2 小时前
前端一些用得上的有意思网站
前端·javascript·vue.js·学习
张3蜂2 小时前
CSRF Token:网络应用安全的关键防线——深度解析与实战指南
前端·安全·csrf