代码分离与懒加载
约 660 字大约 2 分钟
2026-02-11
代码分离不是“高级技巧”,而是前端性能优化的基础操作。
它主要解决的问题是:把“首屏不需要的代码”从首包移走。
为什么需要代码分离
默认情况下,Webpack 会把入口依赖尽量打进主包。项目一大,首包就会变重,直接影响:
- 首次加载时间。
- 白屏时长。
- 移动端弱网体验。
所以目标很明确:
- 当前页面必须代码进入首包。
- 非当前必需代码异步加载。
三种常见方式
- 多入口(多页面场景)
- 动态导入
import()(按需加载场景) optimization.splitChunks(统一拆包治理)
动态导入:最常用的懒加载手段
动态导入最适合“用户触发后才需要”的模块,如图表、编辑器、重型表单。
const button = document.querySelector('#load-report');
button.addEventListener('click', async () => {
const { renderReport } = await import(
/* webpackChunkName: "report" */
/* webpackPrefetch: true */
'./report'
);
renderReport();
});上面的魔法注释主要解决两个问题:
webpackChunkName:让异步包命名可读。webpackPrefetch:浏览器空闲时预取未来可能用到的资源。
演示:按点击行为再加载模块
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Dynamic Import Demo</title>
</head>
<body>
<button id="load">按需加载模块</button>
<p id="status">状态:未加载</p>
<script type="module">
const button = document.getElementById("load");
const status = document.getElementById("status");
button.addEventListener("click", async () => {
const code = `export const run = () => '模块已加载';`;
const url = URL.createObjectURL(new Blob([code], { type: "text/javascript" }));
const mod = await import(url);
status.textContent = "状态:" + mod.run();
URL.revokeObjectURL(url);
});
</script>
</body>
</html>splitChunks:从“单点懒加载”走向“全局治理”
当项目规模变大,只靠手动 import() 不够,需要统一拆包策略。
module.exports = {
optimization: {
chunkIds: 'deterministic',
splitChunks: {
chunks: 'all',
minSize: 20 * 1024,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
},
utils: {
test: /[\\/]src[\\/]utils[\\/]/,
name: 'utils',
priority: 5,
},
},
},
runtimeChunk: 'single',
},
};| 选项 | 资源用途 | 下载时机 | 优先级 |
|---|---|---|---|
webpackPreload | 当前导航很快要用 | 与父 chunk 并行 | 较高 |
webpackPrefetch | 未来导航可能要用 | 浏览器空闲时 | 较低 |
preload 与 prefetch 的差别
使用注意事项
建议
- 大依赖(图表/编辑器)默认考虑懒加载。
- 拆包策略以体积分析报告驱动。
- 配合
runtimeChunk: 'single'稳定缓存。
常见错误
- 拆包过碎,导致请求数过多。
- 多入口未抽离共享依赖,重复打包。
- 只写了
import()但没有回归验证收益。
最佳实践
- 把代码分离当成“首包预算管理”工具。
- 给关键页面设定首包体积阈值。
- 每次优化都做前后对比,避免伪优化。
