案例研究:我将 React 打包体积降低了 68%
Source: Dev.to
引言
我在一个 B2B 应用上工作时,遇到了一个常见的误解:企业会对糟糕的 Google Core Web Vitals 和臃肿的 bundle 大小更宽容,因为“用户在办公室的电脑上使用高速网络”。实际上,主要用户是通过移动电话和移动网络访问该应用的。当我发现这一点后,优化变得至关重要。
起始点: 生产环境的 bundle 大小为 1,542 KB(gzip 压缩后)。
目标: 500 KB(遵循行业最佳实践)
第一步:压缩 – 已经优化
显而易见的第一步是启用 Brotli 压缩,相比 gzip 通常可以节省 15–25 %。然而基础设施已经开启了 Brotli,所以这里无法再获得收益。
第二步:基于路由的代码拆分
React Router 本身就支持懒加载。我为所有路由实现了动态导入:
// Before
import Dashboard from './Dashboard';
// After
const Dashboard = React.lazy(() => import('./Dashboard'));
结果: Bundle 大小降低了 37 %,至 971 KB。
第三步:依赖分析与 Vendor 拆分
使用 Webpack Bundle Analyzer,我发现了占据 bundle 大部分体积的第三方库:
- 日期选择/日历库
- 功能标记管理
- 分析 SDK
- PDF 编辑器
- 富文本编辑器
- 文件上传组件
这些库在多个模块中被直接导入。我创建了包装模块并使用动态导入:
// Before – Direct imports everywhere
import { DatePicker } from 'heavy-date-library';
// After – Wrapper with lazy loading
export const loadDatePicker = () =>
import('heavy-date-library').then(module => module.DatePicker);
结果: Bundle 缩小至原始大小的 57 %(879 KB)。
第四步:模块解耦与依赖链
Bundle Analyzer 显示出紧耦合的依赖链。例如:
// ❌ Tightly coupled
// Module A imported Module B, which imported Module C,
// which imported a heavy utility library
// All loaded together even if only one piece was needed
// ✅ Solution: Extract shared code to independent modules
// Created small, focused modules with only necessary shared code
// Broke circular dependencies
我识别并拆分了这些链路,创建了仅包含必要共享代码的小而专注的模块。
结果: Bundle 实现 63 % 的减小(571 KB)。
第五步:本地化优化
应用支持多种语言,但所有翻译文件都会同步加载,且不考虑用户的语言偏好。
之前: 所有语言文件一起打包(约 120 KB 的 JSON)。
之后: 仅在初始加载时加载用户选中的语言:
// Dynamic locale loading
const loadLocale = (locale) =>
import(`./locales/${locale}.json`);
最终结果: 493 KB —— 68 % 的减小,成功突破我们的 500 KB 目标!
关键要点
- 假设很危险: 没有数据不要随意假设用户环境。
- 先分析再优化: Webpack Bundle Analyzer 是不可或缺的工具。
- 第三方库代价高: 对重量级依赖要有策略地使用。
- 依赖链很重要: 紧耦合会导致 bundle 膨胀。
- 本地化可能很重: 动态加载语言文件。