产物与代码分割
生产构建要把源码变成 可长期缓存、体积可控、按路由懒加载 的静态文件。工具怎么转译见 构建工具与 Vite;模块格式为何影响摇树见 模块系统。
解决什么问题
| 痛点 | 产物策略 |
|---|---|
首屏下载 5MB 一个 index.js | 拆 chunk:路由级 import()、把 vendor 分开 |
| 发版后用户仍跑旧逻辑 | 文件名带 content hash,HTML 引用新名字 |
| 删了没用的导出,包一点没小 | 不是 ESM / 有 side effect / 被 sideEffects 标错 |
| 线上报错看不懂 | sourcemap 上传到监控,不要默认把 map 公开到生产 CDN(看安全策略) |
一句话:HTML 是入口清单;带 hash 的资源可以缓存很久;chunk 边界决定每次改动让谁缓存失效。
产物里有什么
Vite 默认 dist/ 大致是:
dist/
index.html 引用带 hash 的 js/css
assets/index-xxxx.js
assets/index-xxxx.css
assets/vendor-xxxx.js (若被拆出)
| 文件 | 缓存直觉 |
|---|---|
index.html | 往往不长期缓存或协商缓存;每次发布要换到新 hash |
assets/*-hash.js | 内容变才变 hash,可 Cache-Control: immutable |
放 public/ 的文件 | 原样拷贝,不经过打包;适合 robots.txt、不会 import 的静态件 |
public/ 里的路径不要指望 tree-shake;大图、PDF 放对地方。
Tree-shaking
打包器删除 证明没被用到的导出。前提:
- 源码是 ESM(
import/export) - 模块没有无法分析的副作用,或
package.json的sideEffects标对
{
"sideEffects": ["*.css", "./src/polyfill.js"]
}
标 sideEffects: false 等于声称「只导入副作用也可以删整文件」。标错会把 polyfill / CSS 摇掉,运行时才炸。
默认导出、export * from、动态 import(变量) 都会削弱分析。库作者想被摇树:提供 ESM、避免在顶层乱写副作用。
代码分割
const Page = lazy(() => import("./pages/Settings.tsx"));
import() 是运行时边界,打包器据此切 async chunk。用户进设置页才下载。
| 策略 | 用途 |
|---|---|
| 路由级动态 import | 最常见,按页面切 |
手动 manualChunks | 把 react 等稳定依赖打进 vendor,业务改动不让它失效 |
| 组件级再切 | 很重的编辑器、图表;切太碎会变成请求瀑布 |
经验:先按路由切;vendor 是否独立用打包分析(rollup-plugin-visualizer / Vite 的 bundle 可视化)看,不要一上来十几个 HTTP/1 小文件——HTTP/2 下仍要权衡压缩字典和解析成本。
Hash 与缓存
index-a1b2c3.js → 改源码 → index-d4e5f7.js
index.html 里的 <script src> 跟着变
坑:
- 只缓存了 HTML,资源 hash 变了用户仍拿旧 HTML → 404;HTML 要用短缓存或部署时确保新 HTML 先/同时上线
- CDN 与源站缓存策略不一致
- Service Worker 另有缓存,发版要有更新策略
Sourcemap
| 场景 | 建议 |
|---|---|
本地 build 排查 | sourcemap: true |
| 生产 | map 上传 Sentry 等,CDN 不提供 .map 或鉴权 |
map 含源码路径甚至源码,相当于把仓库公开一截。
要点
- HTML 引用 hash 资源;hash 资源可长缓存
- 摇树依赖 ESM + 正确的
sideEffects import()是切包的主手段,从路由开始public/不打包,大文件放错目录会直接打进站点根- sourcemap 是调试资产,不是默认公开文件
面试速记
- 为什么文件名要带 hash? 内容变则 URL 变,才能让浏览器/CDN 长缓存且发版立即换新。
- Tree-shaking 是什么? 删除未被引用的 ESM 导出;CJS 很难做。
- 代码分割怎么做? 动态
import()(路由/重功能);可选 vendor 拆包。 public和src/assets差在哪?public原样拷贝;src经打包、可 hash、可参与模块图。- 首屏很大怎么下手? 看分析图:先砍同步依赖,再按路由懒加载,而不是先开混淆档。