Webpack 构建优化:包体积优化
1. 面试题
Q: 如果打包出来的 JS 体积过大,你会从哪些方面进行优化?
2. 核心解答
包体积优化是为了:更快加载。核心手段是 压缩 和 拆分。
(1) 代码分割 (Code Splitting)
把庞大的 Bundle 切分成多个小的 Chunk,按需加载或缓存。
- SplitChunksPlugin (Webpack 4+): 将第三方库 (vendors) 和公共代码 (commons) 从 main.js 中提取出来。
optimization: {
splitChunks: {
chunks: 'all', // 同步和异步都拆
cacheGroups: {
vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }
}
}
}
- Dynamic Import: 使用
import() 语法实现路由懒加载。Webpack 会自动将该模块打包成单独的文件,仅在路由激活时才请求。
(2) 摇树优化
- Dead Code Elimination: 移除未使用的代码。
- Requirement: 必须使用 ESM (
import/export)。CommonJS 不支持 Tree Shaking。
- SideEffects: 在
package.json 中声明 sideEffects: false,告诉 Webpack 该库没有任何副作用,即使被引用了但没使用导出,也可以安全地删掉整个文件。
- CSS 问题: 需要把 css 后缀加入
sideEffects 数组,防止 CSS 被误删。
(3) 文件压缩
- JS:
TerserPlugin (默认开启),移除空格、注释、缩短变量名。
- CSS:
CssMinimizerPlugin (需手动配置),压缩 CSS。
- Gzip: 服务端开启 gzip,能在 HTTP 传输层面减少 70% 的体积。
(4) 作用域提升
- ModuleConcatenationPlugin (Webpack 3+,生产模式默认开启)。
- 原理:将多个模块的代码放在同一个函数作用域中,减少了函数声明的开销和代码体积。
- 条件:必须使用 ESM。
(5) 移除冗余资源
- ContextReplacementPlugin: (经典案例 Moment.js)。Moment 默认包含几十种语言包,只保留
zh-cn 即可减少几百 KB。
- IgnorePlugin: 忽略某些模块。
3. 面试加分项
Q: Tree Shaking 为什么必须是 ESM?
因为 ESM 是静态结构 (Static Structure),编译时就能确定依赖关系。而 CommonJS 是动态的 (require(condition ? 'a' : 'b')),无法在不运行代码的情况下确定到底引用了什么。
4. 总结
- Split: Vendor Chunk, Async Chunk.
- Remove: Tree Shaking, SideEffects: false.
- Mini: Terser, CssMinimizer.
- Hoist: Scope Hoisting.