86 lines
3.8 KiB
Markdown
86 lines
3.8 KiB
Markdown
---
|
||
id: frontend.webpack.scenario.build-speed
|
||
title: 大型项目构建慢、包体积大:度量、优化和防退化
|
||
type: scenario
|
||
domain: frontend
|
||
stack: [webpack, fe-testing-perf, cicd]
|
||
tags: [构建速度, 包体积, splitChunks, 持久化缓存, 体积预算, CI]
|
||
level: intermediate
|
||
prerequisites: [frontend.webpack.build-optimization]
|
||
est_hours: 2
|
||
related: [frontend.webpack.interview, frontend.vite.scenario.webpack-to-vite]
|
||
week: [7]
|
||
---
|
||
|
||
# 大型项目构建慢、包体积大:度量、优化和防退化
|
||
|
||
## 业务背景与问题
|
||
|
||
一个中台项目(webpack 5,几百个页面):CI 构建要十几分钟,发版排队;首页 JS 有几 MB,弱网用户首屏很慢;优化过一次,几个月后又变回去了。
|
||
|
||
## 约束
|
||
|
||
- 短期内不能换构建工具(有大量自定义 loader 和插件)。
|
||
- 优化要可量化,并且防止退化。
|
||
|
||
## 方案设计
|
||
|
||
1. **先度量**:
|
||
- 构建速度:统计各 loader / plugin 耗时(速度分析插件或 `--profile`);
|
||
- 体积:生成产物分析报告(`webpack --json` + 可视化工具),找出最大的依赖和重复依赖。
|
||
2. **构建提速**:
|
||
- 开启 `cache: { type: "filesystem" }`,CI 中缓存缓存目录;
|
||
- babel-loader 换成 swc 类 loader,并用 `include` 只处理源码;
|
||
- 生产构建关闭不需要的 source map 或使用更快的类型;
|
||
- 类型检查与构建并行(单独的 `tsc --noEmit` 任务)。
|
||
3. **体积优化**:
|
||
- 路由级懒加载;
|
||
- 大依赖按需引入(图表库、组件库、日期库)或替换为更小的库;
|
||
- `splitChunks` 按变化频率拆分第三方库;
|
||
- 检查 `sideEffects` 让 tree shaking 生效。
|
||
4. **防退化**:在 CI 中设置体积预算(`performance.maxEntrypointSize` 或脚本对比上次的产物大小),超出就失败并提示。
|
||
|
||
## 关键代码
|
||
|
||
```js
|
||
// webpack.config.js 片段
|
||
export default {
|
||
cache: { type: "filesystem", buildDependencies: { config: [import.meta.filename] } }, // 配置变化时缓存失效
|
||
module: {
|
||
rules: [{ test: /\.[jt]sx?$/, include: path.resolve("src"), use: "swc-loader" }],
|
||
},
|
||
optimization: {
|
||
runtimeChunk: "single",
|
||
splitChunks: {
|
||
chunks: "all",
|
||
cacheGroups: {
|
||
framework: { test: /[\\/]node_modules[\\/](vue|vue-router|pinia)[\\/]/, name: "framework", priority: 20 },
|
||
vendors: { test: /[\\/]node_modules[\\/]/, name: "vendors", priority: 10 },
|
||
},
|
||
},
|
||
},
|
||
performance: { hints: "error", maxEntrypointSize: 500 * 1024, maxAssetSize: 300 * 1024 }, // 超预算直接构建失败
|
||
};
|
||
```
|
||
|
||
可运行的基础配置:[examples/webpack-config](../examples/webpack-config/)。
|
||
|
||
## 取舍
|
||
|
||
| 选择 | 优点 | 缺点 |
|
||
| --- | --- | --- |
|
||
| **在 webpack 内优化(本方案)** | 风险小,见效快 | 上限受 webpack 限制 |
|
||
| 迁移到 Rspack | 配置大体兼容,速度提升大 | 部分插件需要验证 |
|
||
| 迁移到 Vite | 开发体验最好 | 迁移成本高,见[迁移场景](../../vite/scenarios/webpack-to-vite.md) |
|
||
|
||
## 易错点
|
||
|
||
- 没有度量就开始优化,改了很多却不知道哪项有效。
|
||
- 持久化缓存没有配置 `buildDependencies`,改了配置却用了旧缓存。
|
||
- 把所有第三方库打成一个巨大的 vendor,任何依赖升级都让整个文件缓存失效。
|
||
- 只优化一次,没有预算和 CI 检查,很快退化。
|
||
|
||
## 面试怎么讲
|
||
|
||
> “先度量:用速度分析和产物分析找到瓶颈,主要是 babel 处理了 node_modules 和一个整包引入的图表库。然后开启持久化缓存并在 CI 中缓存、换成 swc 类 loader、类型检查并行;体积上做路由懒加载、图表库按需引入、按变化频率拆包。最后在 CI 加体积预算防止退化。数据以实际测量为准,我会给出优化前后的对比。”
|