前端性能优化:从打包到生产部署
前端性能题容易答成「压缩、懒加载、CDN」三条就结束。面试官更想听你能把 构建产物 → 传输 → 浏览器解析 → 上线流程 串起来。我按自己项目(Vite + React/Vue)的习惯写,框架无关的部分也保留了。
题目
前端性能优化怎么做?从打包构建到生产环境部署,你会关注哪些点?
参考答案(口述要点)
- 构建:代码分割、Tree Shaking、压缩、图片资源优化、分析 bundle
- 传输:Gzip/Brotli、CDN、HTTP 缓存(hash 文件名 + 长缓存)
- 运行时:懒加载路由/组件、减少重渲染、长列表虚拟滚动、避免大依赖
- 指标:LCP、FID/INP、CLS,用 Lighthouse / Performance 面板验证
- 部署:CI 构建、静态资源上 CDN、Nginx 缓存策略、灰度与回滚
详细解析
构建阶段(打包)
| 优化项 | 做法 | 效果 |
|---|---|---|
| 代码分割 | 路由 import() 动态加载 | 首屏 JS 变小 |
| Tree Shaking | ESM + sideEffects: false | 去掉死代码 |
| 压缩 | build.minify: 'esbuild' | 体积下降 |
| 大图 | WebP、按尺寸导出、雪碧图/svg | 减少传输 |
| 分析包体积 | rollup-plugin-visualizer | 找巨型依赖 |
Vite 配置示例(面试可口述)
// vite.config.ts
export default defineConfig({
build: {
target: 'es2015',
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
antd: ['antd'],
},
},
},
},
});把 react、antd 拆成独立 chunk,升级业务代码时用户不用重新下整个 vendor(配合缓存)。
运行时性能
- 减少首屏请求:路由懒加载、非首屏组件
React.lazy/defineAsyncComponent - 列表:上千条用虚拟滚动(
react-window、虚拟列表),别一次渲染 DOM - 状态:避免父组件频繁 setState 导致整树重渲染;
memo、useMemo用在热点路径 - 第三方库:moment 换 dayjs;整包 import
lodash改lodash-es按函数引
网络与缓存
文件名带 content hash(Vite 默认):
assets/index-a1b2c3d4.js → Cache-Control: max-age=31536000, immutable
index.html → no-cache,每次验证Nginx 示例:
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache";
}- Gzip / Brotli:Nginx
gzip on;或 CDN 开启,JS/CSS 体积可再降 60%+ - CDN:静态资源走 CDN 域名,就近接入;API 仍回源
部署流程(能说清即可)
git push → CI(pnpm install → pnpm build)→ 产物 dist/
→ 上传 OSS/CDN 或打进 Docker / Spring Boot static
→ Nginx 反代或对象存储 + HTTPS
→ 可选:灰度、健康检查、回滚上一版 hash 包个人项目里常见两种:
- 纯静态:
dist扔 Nginx / OSS,SPA 配try_files回index.html - 前后端一体:
vite build输出到src/main/resources/static,一个 jar 部署(如 agent-web-flux)
怎么验证优化有效
| 工具 | 看什么 |
|---|---|
| Lighthouse | LCP、TBT、CLS 分数 |
| Chrome Performance | 长任务、布局抖动 |
| Network | 瀑布图、是否 304、是否走 CDN |
pnpm build 后看 dist 体积 | 单 chunk 是否超过 500KB 警告线 |
代码与实践
路由懒加载(Vue 3)
const routes = [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue'),
},
];生产环境关掉 source map(减体积、防源码暴露)
build: {
sourcemap: false,
}SSE / 流式页面注意点(做过 AI 聊天的话可提)
- Nginx 对
/api/chat/stream要proxy_buffering off,否则流式变一次性输出 - 前端用
fetch+ReadableStream,别用会缓冲的封装
项目里踩过的坑
- Ant Design 全量引入导致首屏 1MB+ → 改按需或拆
manualChunks - 图片未压缩直接进仓库 → 构建插件
vite-plugin-imagemin或 CI 前压缩 index.html也被 CDN 强缓存 → 发版后用户看到旧版,必须 html no-cache
延伸 / 易错点
- 只优化构建不优化接口:首屏等 5 个串行 API,前端再快也白搭 → 合并接口、并行请求、SSR 注水数据。
- hash 缓存忘了清:只改 JS 没问题;若改
index.html引用路径,要确保部署流程原子更新。 - 过度 code split:chunk 太多,HTTP/1.1 下连接数反而吃亏;HTTP/2 好一些,但要平衡。
写在最后
回答时按 打包 → 缓存/CDN → 运行时 → 部署验证 四条线说,中间夹一个你项目里的数字(比如首屏从 3s 到 1.2s)更有说服力。别只背「懒加载、防抖节流」。
创作不易,转载请注明出处和作者。
