当前位置: 首页 > news >正文

Webpack 自定义插件开发指南:构建流程详解与实战开发全攻略

一. webpack打包流程

开发 Webpack 插件的第一步,就是明确:我的插件要接入 Webpack 构建流程的哪个阶段,解决什么问题。

  • 了解流程之前首先要了解插件的两个核心概念:compilercompilation
1. compiler:全局构建控制器
你可以这样理解它:
  • 是 Webpack 的“大脑”,负责整个构建流程
  • 只会在整个 Webpack 启动时创建一次
  • 提供入口:如 compiler.hooks.compile、compiler.hooks.run 等

注册插件

class MyPlugin {apply(compiler) {// 插件注册时调用一次}
}

使用钩子

compiler.hooks.beforeRun.tap('MyPlugin', () => { ... })
2. compilation:本次构建上下文
你可以这样理解它:
  • 是每次构建过程中的“局部快照”
  • 管理模块 (module)、代码块 (chunk)、资源 (asset)等
  • 在监听模式或热更新时,每次都会重新生成一个 compilation 实例

你要修改文件,生成资源,访问chunk的时候,一定是在 compilation 阶段完成的

compiler.hooks.thisCompilation.tap('MyPlugin', (compilation) => {// compilation 是这一次构建的上下文compilation.hooks.buildModule.tap(...)compilation.hooks.processAssets.tap(...)
});
举个例子
class FileListPlugin {apply(compiler) {// 第一次执行,仅执行一次,注册插件钩子compiler.hooks.thisCompilation.tap('FileListPlugin', (compilation) => {// 每次构建及热更新都会触发compilation.hooks.processAssets.tap({name: 'FileListPlugin',stage: compilation.constructor.PROCESS_ASSETS_STAGE_SUMMARIZE},() => {const files = Object.keys(compilation.assets).join('\n');compilation.emitAsset('filelist.md', new compiler.webpack.sources.RawSource(files));});});}
}
webpack打包流程
阶段对应方法插件钩子说明
1️⃣ 初始化webpack() 创建 compilercompiler.hooks.environment读取配置、初始化插件
2️⃣ 编译开始compiler.run()compiler.hooks.beforeRunruncompile启动构建,调用编译
3️⃣ 创建 Compilationnew Compilation()thisCompilationcompilation代表一次构建,管理模块、资源
4️⃣ 构建模块buildModule/handleModuleCreationcompilation.hooks.buildModule构建入口与递归依赖
5️⃣ 完成模块seal()sealoptimizeModules完成依赖分析与模块关系确定
6️⃣ 生成代码块createChunkGraph()optimizeChunks把模块打成 chunk
7️⃣ 生成资源emitAssets()processAssets将 chunk 渲染为文件(此时文件还在内存中)
8️⃣ 输出结果outputFileSystem.writeFileafterEmitdone写入磁盘,构建完成

二. 插件的开发

1. 插件的基本结构
class MyPlugin {constructor(options) {this.options = options;}// 创建插件类并实现 apply(compiler) 方法apply(compiler) {// 注册 compiler 生命周期钩子compiler.hooks.thisCompilation.tap('MyPlugin', (compilation) => {// 注册 compilation 生命周期钩子compilation.hooks.processAssets.tap({name: 'MyPlugin',stage: compilation.constructor.PROCESS_ASSETS_STAGE_SUMMARIZE},(assets) => {const { RawSource } = compiler.webpack.sources;// 修改、添加资源const content = 'hello plugin';compilation.emitAsset('my-output.txt', new RawSource(content));});});}
}module.exports = MyPlugin;
  • stage 是一个数字,标识你要插入的逻辑在整个资源处理生命周期中的阶段优先级
  • PROCESS_ASSETS_STAGE_SUMMARIZE的stage为7000,代表总结阶段,可以做输出文件列表、日志、清单等操作
  • 其他更多的stage可以翻阅webpack官网查看
2. 注册钩子的方法
tap: 同步钩子
compiler.hooks.beforeRun.tap('MyPlugin', (compiler) => {console.log('Before run');
});
tapAsync:异步钩子(使用回调)
compiler.hooks.beforeRun.tapAsync('MyPlugin', (compiler, callback) => {setTimeout(() => {console.log('Before run (async)');callback();}, 1000);
});
  • 用于需要手动处理异步任务的情况,必须调用 callback()
tapPromise:异步钩子(使用 Promise)
compiler.hooks.beforeRun.tapPromise('MyPlugin', (compiler) => {return new Promise((resolve) => {setTimeout(() => {console.log('Before run (promise)');resolve();}, 1000);});
});
  • 推荐用于现代异步开发场景

三. 利用webpack插件实现上线后热更新的功能

1. 构建完成后自动生成一个带 hash 的版本号文件
class VersionFilePlugin {constructor(options = {}) {this.filename = options.filename || 'version.txt';}apply(compiler) {compiler.hooks.thisCompilation.tap('VersionFilePlugin', (compilation) => {const { RawSource } = compiler.webpack.sources;compilation.hooks.processAssets.tap({name: 'VersionFilePlugin',stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS},() => {// 获取当前构建 hashconst hash = compilation.fullHash;const content = `version: ${hash}`;// 添加 version.txt 文件compilation.emitAsset(this.filename, new RawSource(content));});});}
}module.exports = VersionFilePlugin;
注册插件
const VersionFilePlugin = require('./plugins/VersionFilePlugin');module.exports = {// ...plugins: [new VersionFilePlugin({filename: 'version.txt'})]
};
2. 定时轮询 version.txt,检测 hash 是否变化
<script>
(function () {const VERSION_CHECK_INTERVAL = 1000 * 60 * 5; // 每 5 分钟检查一次const VERSION_URL = '/version.txt';let currentVersion = null;async function checkVersion() {try {const res = await fetch(VERSION_URL, { cache: 'no-store' });const text = await res.text();const latestVersion = text.trim().split('version: ')[1];if (currentVersion && latestVersion !== currentVersion) {console.warn('[version check] 新版本已发布,自动刷新页面');location.reload(true); // 强制刷新页面}currentVersion = latestVersion;} catch (err) {console.error('[version check] 检查失败:', err);}}// 初始加载checkVersion();// 定时检查setInterval(checkVersion, VERSION_CHECK_INTERVAL);
})();
</script>

⚠️ 需要将该文件一并部署到 Web 服务器根目录

有好多全局构建的生命周期钩子每次构建生成的模块、资源、chunk 等上下文生命周期钩子本文没有展示,想了解更多的可以去看下webpack官方文档

相关文章:

  • Apache Kafka 面试应答指南
  • 《人间词话》PPT课件
  • 免费无广告PDFCreator:虚拟打印软件一键转 PDF/PNG/JPG
  • K8s port、targetPort和nodePort区别
  • AI歌手Yuri出道:GenAI,透露着新的AI产业机遇?
  • 【水印论文阅读1】将水印规则的定义域从离散的符号空间转移到连续的语义空间
  • 在shell中直接调用使用R
  • Vite 打包原理详解 + Webpack 对比
  • 电子电气架构 --- 涵盖“诊断与 ECU 平台”领域特有项目要求(下)
  • FPGA 40G到100G光纤数据传输QSFP
  • Netty入门案例:简单Echo服务器(同步)
  • 【Linux高级全栈开发】2.2.3 UDP的可靠传输协议QUIC
  • 路径遍历攻击与修复
  • 非功能测试
  • 【论文阅读 | CVPR 2025 |MambaVision:一种混合 Mamba-Transformer 视觉骨干网络】
  • 【Android】蓝牙相关
  • 基于大模型的肺结核诊疗全流程预测与干预研究报告
  • 什么是国际期货?期货交易平台搭建
  • debian挂载新硬盘后不识别怎么办?
  • 将ONNX模型转换为(OPENMV可用的格式)TensorFlow Lite格式