深浅色
工程化与性能(背诵版)
1. Vite 为什么快
- dev 阶段不做打包:浏览器直接请求原生 ESM,按需编译,启动秒开。
- 依赖用 esbuild 预构建:把 CJS / 多文件的包合成单文件,减少请求数。
- 生产用 Rollup 打包(插件生态和代码分割更成熟)。
- HMR:只让变更模块的边界失效,不做全量刷新。
- 追问:为什么生产不直接用 esbuild?-> 生态、插件和代码分割能力当时不如 Rollup。
2. Tree shaking
- 前提三条:ESM 静态结构、包声明
sideEffects: false、打包器能静态分析。 - CJS 摇不掉,因为 require 是运行时动态的,打包器无法确定用了哪些导出。
- 追问:为什么 import 整个 lodash 摇不掉?-> 它是 CJS 且副作用声明不干净;要按需引入或用 lodash-es。
3. 代码分割与首屏优化
- 路由级懒加载(React.lazy + Suspense)、手动拆 vendor chunk。
- 首屏优化:按需加载、关键资源 preload、图片懒加载、SSR。
- 追问:preload 和 prefetch 的区别?-> preload 是当前页面马上要用的(高优先级);prefetch 是下个页面可能用的(空闲时下)。
4. 前端监控
- 错误捕获三件套:window.onerror(同步错误)、window.addEventListener('error')(资源加载失败)、unhandledrejection(未处理的 Promise)。
- 上报用 navigator.sendBeacon,不阻塞页面卸载;配合采样和去重。
- sourcemap 上传到监控平台,用 stack trace 还原源码位置。
- 追问:为什么用 sendBeacon?-> 页面卸载时普通异步请求会被取消,sendBeacon 由浏览器保证发出。
5. 测试分层
- 单测(vitest / jest):纯函数、hook、组件渲染逻辑。
- 集成(Testing Library):按用户行为断言,不测实现细节。
- E2E(playwright):只覆盖关键路径(登录、下单)。
- 追问:为什么单测数量最多?-> 快、稳定、定位准;E2E 慢且容易脆。
6. CI/CD 与 Nginx
- 流程:push -> lint / test -> build -> 产物上传 CDN -> 灰度 -> 全量。
- Nginx 关键配置:gzip / brotli、静态资源长缓存、history 路由回退
try_files $uri /index.html、反向代理 API。 - 追问:SPA 为什么必须 try_files?-> /user/1 在服务端没有对应文件,直接访问会 404。
7. Monorepo
- pnpm workspace:packages 下共享依赖,硬链接省磁盘。
- 共享类型包:把 API 类型抽成 packages/types,前后端都依赖,改一处两端同步。
- 追问:Monorepo 最大的成本?-> CI 时间和构建缓存,要靠 turbo / nx 做增量。
8. BFF
- BFF = 为前端定制的聚合层,做数据裁剪、聚合多个服务、鉴权。
- 好处:前端少发请求、字段按需;代价:多一层维护和延迟。
- 追问:什么时候不需要 BFF?-> 后端已提供 GraphQL,或接口粒度本来就合适。