读 awesome-webpack-cn:从入门到 Loader/Plugin 的构建地图
本文目录
从模块图、Loader/Plugin 到缓存、分包、HMR、Source Map 与 Module Federation,建立适用于 Webpack 5 的构建排障和迁移路线。
本文围绕 webpack-china/awesome-webpack-cn 做原创工程整理。项目来自 腾讯新闻前端团队 2022 年的 25 个 Awesome 项目推荐;上游 README 固定到 2022-07-15 commit 34889dbb98ed2b5f83fb900e540652f834e99a3d;仓库未声明 SPDX 许可且根目录没有 LICENSE 下方保留固定版本入口。
仓库内容地图
- 入门:Webpack 1.x 到 5.x 的版本脉络与基础配置
- 进阶:构建性能、项目优化、文件体积、工程化实践
- 框架集成:React、Vue、Angular
- 原理开发:模块图、生成代码、Loader 与 Plugin、发布 npm 包
- 生态:Starter Kit、中文文档与关联资源
怎么读才有收获
先确定项目实际使用的 Webpack 主版本。读旧文章是为了理解构建问题的来历,不是照抄配置。现代项目要优先测量真实瓶颈:冷启动、增量编译、缓存命中、依赖分包和运行时成本。
一、先校准版本,再看配置
Webpack 是静态模块打包器:从一个或多个 entry 开始,解析 import/require 形成依赖图,再把模块转换成 chunks 和静态资源。看配置前先确认仓库 lockfile 中 webpack、webpack-cli、webpack-dev-server 和相关 loader/plugin 的实际版本;README 里的 1.x-4.x 文章不能直接当成 Webpack 5 配置手册。
本篇的原仓库快照固定在 2022-07-15,收录了 158 条外链引用、157 个不同目标,其中包括较早版本教程、Starter Kit 和第三方文章。仓库没有声明 SPDX 许可,也没有根目录 LICENSE。为避免把无许可声明的整份第三方文章目录整体搬到本站,本文重写为独立的现代工程指南,只保留固定 Git 来源和必要的官方参考;旧索引中的链接不能视作已验证或推荐。
二、把构建流程画成一条可调试的链
- Entry:决定从哪些页面、Worker 或运行时入口开始。多入口适合独立页面或不同 target,但要检查公共代码是否重复。
- Resolve:按扩展名、目录、package exports、别名和 target 条件找到实际文件。不同环境可能命中不同入口,排错时要看最终解析路径。
- Module graph:静态依赖成为图的一部分;动态 import 会形成异步边界。循环依赖、重复版本和 barrel re-export 都会影响结果。
- Loaders / module types:对匹配的资源做转换,或用 Webpack 5 asset modules 把图片、字体等变成 URL、输出文件或内联数据。
- Plugins:订阅 compiler/compilation 生命周期,跨模块生成、优化、校验或输出资源。
- Chunks / output:优化依赖图后发出带 hash 的脚本、样式、字体、映射文件和 manifest;部署端还需让 HTML 引用与资源版本一致。
错误定位从最靠近症状的一层开始:找不到模块先看 Resolve;某个文件内容不对看 loader/rule;产物缺失看 asset type/plugin;线上加载 404 则核对 publicPath、CDN 路径和发布原子性。
三、Loader 和 Plugin 不是一回事
Loader 面向单个模块资源:匹配文件、执行转换、把结果交给后续 Loader,链条按从右向左运行。它适合 TypeScript/Sass 转换、代码预处理和自定义资源类型。规则应尽量窄化 test、include、exclude 与 oneOf,不要让不相关依赖也经过昂贵转换;生产中的 loader 选项、source map 和缓存要与真实版本核对。
Plugin 面向编译器与整个 compilation:可在适当 hook 上检查模块图、修改 chunk、输出附加文件或执行构建质量门禁。Webpack 5 插件应选择明确的 hook 和 processAssets stage,避免依赖内部字段或过早读取尚未生成的资源。
class BuildInfoPlugin {
apply(compiler) {
const { Compilation, sources } = compiler.webpack;
compiler.hooks.thisCompilation.tap("BuildInfoPlugin", compilation => {
compilation.hooks.processAssets.tap(
{
name: "BuildInfoPlugin",
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
},
() => {
const content = JSON.stringify({
generatedAt: new Date().toISOString(),
});
compilation.emitAsset("build-info.json", new sources.RawSource(content));
},
);
});
}
}
module.exports = BuildInfoPlugin;
自定义插件不要把密钥或私有环境变量写进浏览器产物。若插件会改写既有 asset,应处理重名、并行编译和缓存失效,并为失败提供明确的构建错误,而不是悄悄生成半成品。
四、Webpack 5 配置示例:作为起点,不是万能模板
下面的配置只展示生产 hash、清理输出目录、资源模块和 chunk 分组。实际项目要结合多入口、CSS 处理、运行环境、CDN 路径、浏览器兼容目标和 SSR 分别验证。runtimeChunk 可能增加请求,不应不看页面结构就照搬。
const path = require("node:path");
module.exports = {
mode: process.env.NODE_ENV === "development" ? "development" : "production",
entry: { app: "./src/main.js" },
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash:8].js",
chunkFilename: "[name].[contenthash:8].js",
clean: true,
},
module: {
rules: [
{ test: /\\.(png|svg|jpe?g|gif)$/i, type: "asset/resource" },
],
},
optimization: {
splitChunks: { chunks: "all" },
runtimeChunk: "single",
},
};
Webpack 5 的 asset modules 取代许多 file-loader、url-loader、raw-loader 用法。若旧 Loader 和 asset modules 同时处理同一资源,可能重复发出文件;迁移要逐条核对 type、dependency 和 URL 输出。CSS 能力受 Webpack 具体版本与实验配置影响,Sass/Less/PostCSS 等预处理仍需相应转换链,不能把某个版本的官方示例套给旧项目。
五、性能优化先分清“构建快”还是“页面快”
开发构建
记录冷启动、二次构建、单文件修改后的增量构建、内存峰值和错误反馈时间。Webpack 5 filesystem cache 对跨进程重复构建有帮助,但缓存命中依赖正确的 build dependencies、环境和配置失效条件。缓存损坏或旧缓存复现不了时,先记录并清理本地缓存,再比较冷/热结果;不要把 cache 目录提交进仓库。
缩短编译范围:缩小 Loader 的 include,避免把 node_modules 全量转译,减少重复 Babel/TypeScript 检查,谨慎使用重型 source map 和进度插件。Worker/并行 loader 有启动与序列化开销,小模块或高频增量构建未必受益。
生产页面
生产模式的压缩、tree-shaking、sideEffects 和分包解决不同问题。先用 bundle report 确认真实的大模块与重复依赖,然后按路由或低频动作做动态导入;异步分块必须有加载态、失败处理和缓存策略。避免一次拆出数百个极小 chunk,也避免首屏同步加载仅在某个设置页使用的编辑器。
文件名使用 content hash 有利于长期缓存,但 HTML 或 manifest 也要在同一版本中原子发布:先上传不可变 hashed assets,再切换 HTML/manifest,保留旧资源一段时间以容纳仍打开的页面。否则滚动发布时旧 HTML 引用的新文件可能返回 404。
诊断步骤
stats先给出模块、chunk、构建时间和错误;输出按需裁剪,避免庞大报告拖慢 CI。- Bundle visualizer 用来找体积来源和重复版本,之后还要确认它是否位于首屏关键路径。
- Chrome Performance 看脚本下载、解析/执行、long task 与交互,而不仅是压缩后的 KB。
- 对改动前后固定同一个 commit、lockfile、Node/OS、构建参数和测试设备;报告中位数与方差。
- 每次只改一个因素,区分“缩小产物”“缩短构建”“改善交互”三种目标。
六、HMR、Source Map 与环境隔离
Hot Module Replacement 是开发期替换模块的协议,不等于页面不重载,也不保证组件状态总能保留。更新无法被依赖链接受、模块有未清理副作用或框架集成未配置时,运行时会回退到完整刷新。热更新路径需监听 dispose/cleanup,避免重复注册 listener、timer 和 socket。
Source map 选择要权衡调试能力、构建开销、映射文件访问权限与源码泄露。可将映射上传到受控错误监控系统而不公开部署;不要因为 production 需要排错就把完整 source map 默认暴露给所有访客。开发服务器、watch 和 HMR 配置也不应带入生产服务。
DefinePlugin 等编译期替换只是在代码中嵌入字符串,不会提供秘密管理。客户端能下载到的常量都可以被访客读取;服务端凭证、私钥、内部 API token 不能放进前端构建环境。
七、Module Federation 要按运行时边界设计
Module Federation 适合需要独立构建/发布并在运行时组合的前端团队,但它不是“更高级的代码拆分”。要约定 remote entry 的版本发布、回滚、网络失败降级、公共依赖版本策略、singleton 冲突、样式/CSS 命名、路由与鉴权契约。远程脚本是运行时供应链的一部分,来源、CSP、完整性、发布权限和可观测性都要纳入威胁模型。
共享 React/Vue 等大依赖可减少重复,但过度共享会形成隐性耦合,版本协商失败会导致运行时错误。先判断是否真需要独立发布;若只需要按路由加载模块,普通动态 import 和稳定部署边界通常更简单。
八、从旧教程迁移到当前工程
- 固定当前 Node、Webpack、CLI、dev-server、插件和 loader 版本,保存 lockfile 与构建基线。
- 找出配置来源:手写 webpack 配置、CLI 生成、框架封装、eject 产物或多层 merge。先确定最终配置。
- 将旧字段、弃用 Loader 和插件按官方 migration guide 分组迁移,不要同时升级框架和构建器。
- 为入口、静态资源、动态 chunk、CSS、source map、publicPath、HMR、SSR 和生产部署分别加验证。
- 比较构建耗时、包体、首屏网络瀑布和回滚成功率,达到目标后再删除旧路径。
若项目本身不需要自定义 module graph、复杂 loader 或 Webpack 插件生态,已有框架提供的构建封装可能更省心。用更少配置不等于少控制:关注它暴露的升级路径、插件边界和构建产物诊断能力。
Webpack 官方文档
- Core Concepts:Entry、Output、Loader、Plugin、Mode
- Loaders
- Plugins
- Code Splitting 与 Bundle Analysis
- Caching
- Asset Modules
- Hot Module Replacement
- Module Federation
- Production
来源快照与边界
本文对 webpack-china/awesome-webpack-cn 的内容主题重新归纳为构建链路、性能、框架集成、源码、Loader/Plugin、Starter Kit 和文档迁移。该仓库 README 最后更新于 2022-07-15,GitHub 未声明 SPDX 许可且根目录没有 LICENSE;其 158 条外链大多指向独立作者的文章、旧教程或项目。本站不镜像这些第三方正文或整份链接编排,避免把历史链接误当成当前可用、兼容或获授权的资源。下方提供固定上游入口,读者可按兴趣自行核验。
动手路线
- 为现有项目产出构建耗时和 bundle 分析报告。
- 写一个只做简单文本转换的 Loader 和一个统计构建产物的 Plugin。
- 把旧版配置迁移实验放在隔离分支,不影响生产发布链。
使用边界
上游是历史资源索引,不代表 1.x-4.x 教程、Starter Kit 或依赖目前仍适用;先查项目真实版本和当前官方迁移指南。
原始入口
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。