当前位置: 首页 > 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官方文档

http://www.lqws.cn/news/545329.html

相关文章:

  • Html5播放器禁止拖动播放器进度条(教学场景)
  • 神经网络的概念和案例
  • FrozenBatchNorm2d 详解
  • 聚铭网络入选嘶吼《中国网络安全细分领域产品名录》“云平台安全管理”与“态势感知”双领域TOP10
  • Linux tcp_info:监控TCP连接的秘密武器
  • CatBoost:征服类别型特征的梯度提升王者
  • 蓝牙工作频段与跳频扩频技术(FHSS)详解:面试高频考点与真题解析
  • System.Threading.Tasks 库简介
  • ubuntu ollama 遇到的若干问题
  • Linux命令行操作基础
  • WPF 3D 开发全攻略:实现3D模型创建、旋转、平移、缩放
  • 记录一个C#/.NET的HTTP工具类
  • Feign 实战指南:从 REST 替代到性能优化与最佳实践
  • 文法、正规式相关习题
  • Linux系统(信号篇)信号的保存
  • WinAppDriver 自动化测试:JavaScript 篇
  • gRPC技术解析与python示例
  • Python基础知识之文件
  • JMH (Java Microbenchmark Harness)
  • .NET MAUI跨平台串口通讯方案
  • (LeetCode 面试经典 150 题 ) 238. 除自身以外数组的乘积 (前缀和)
  • LeetCode 312 戳气球题解(Swift)+ 区间 DP 原理详解 + 可运行代码
  • WSL升级到24.04
  • 使用 asp.net core webapi 导出数据文件
  • .NetCore+Vue快速生产框架开发详细方案
  • LeetCode 349题解 | 两个数组的交集
  • 苍穹外卖day5--Redis设置店铺营业状态
  • 基于ubuntu 22.04环境安装NEURON仿真器
  • jenkins中执行python脚本导入路径错误
  • 跟着AI学习C#之项目实战-电商平台 Day3