前端工程化与性能优化 面试核心
系统掌握工程化体系、构建原理、性能优化与监控,从容应对中高级前端面试
1 前端工程化概述
小作坊 vs 现代工厂
一个前端"小作坊":手动复制文件到服务器,代码没有统一格式,全靠人肉测试,部署靠 FTP 上传。
一个前端"现代工厂":代码有 ESLint 自动检查,提交走 Git Hook,PR 自动跑测试,合并后自动构建部署,线上有实时监控报警。
工程化的本质:用工具和流程替代人工判断,让质量成为默认选项而非偶然结果。
工程化四大支柱
ESM / CommonJS / AMD
Vue / React 组件体系
ESLint / Prettier / Husky
CI/CD / Git Hooks
工程化流水线
每一步都应该是自动化的、可重复的、可回溯的
2 构建工具原理
Webpack 构建流程
index.js → 从入口文件开始,递归分析依赖图
.vue → vue-loader | .scss → sass-loader | .ts → ts-loader | 非 JS → JS
dist/ → bundle.[hash].js | vendor.[hash].js | main.[hash].css
// webpack.config.js 核心配置
const path = require('path');
module.exports = {
entry: './src/index.js', // 入口
output: {
path: path.resolve('dist'), // 输出目录
filename: '[name].[contenthash].js',
clean: true, // 每次构建清理 dist
},
module: {
rules: [
{ test: /\.vue$/, use: 'vue-loader' },
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ },
],
},
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' }),
new MiniCssExtractPlugin(),
],
};
Vite:下一代构建工具
🔨 开发模式:打包所有模块 → 启动 dev server
📦 构建:自己实现的打包器
⏱️ 冷启动:项目越大越慢(分钟级)
🔄 HMR:需要重新打包变更模块链
⚡ 开发模式:原生 ESM,按需编译
📦 构建:Rollup(生产级打包)
🚀 冷启动:毫秒级,不受项目规模影响
🔄 HMR:精确到单个模块,极速
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: { port: 3000, open: true },
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'vue-router', 'pinia'],
},
},
},
},
});
Tree Shaking
import/export),CommonJS 的 require() 是动态的,无法静态分析。
Code Splitting 代码分割
// 1. 动态 import — 路由级懒加载
const routes = [
{ path: '/dashboard', component: () => import('./views/Dashboard.vue') },
{ path: '/settings', component: () => import('./views/Settings.vue') },
];
// 2. Webpack splitChunks 配置
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /node_modules/,
name: 'vendor',
priority: 10,
},
common: {
minChunks: 2,
name: 'common',
priority: 5,
},
},
},
}
案例:构建产物从 5MB 优化到 800KB
3 代码规范
ESLint + Prettier 配置
// .eslintrc.js
module.exports = {
root: true,
env: { browser: true, node: true, es2022: true },
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier', // 必须放最后,关闭与 Prettier 冲突的规则
],
rules: {
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-unused-vars': 'error',
'vue/multi-word-component-names': 'off',
},
};
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 100
}
Husky + lint-staged (Pre-commit Hook)
// package.json
{
"lint-staged": {
"*.{js,ts,vue}": ["eslint --fix", "prettier --write"],
"*.{css,scss}": ["prettier --write"]
}
}
# 安装 & 配置 Husky
npx husky init
# 创建 pre-commit hook
echo "npx lint-staged" > .husky/pre-commit
Commitlint (规范提交信息)
# commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', // 新功能
'fix', // 修复
'docs', // 文档
'style', // 格式(不影响逻辑)
'refactor', // 重构
'perf', // 性能优化
'test', // 测试
'chore', // 构建/工具
]],
},
};
# .husky/commit-msg
npx --no -- commitlint --edit $1
案例:没有规范导致代码风格混乱
某项目三人协作,无 ESLint:A 用双引号 + 分号,B 用单引号无分号,C 用 Tab 缩进。Git diff 充斥格式变更,CR 无法聚焦逻辑,线上因 undefined.xx 报错(ESLint 能检测到)。
引入 ESLint + Husky 后:提交即检查,风格统一,低级错误清零。
4 CI/CD 流水线
流水线图解
GitHub Actions 示例
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npm run lint
- run: npm run test:unit
- run: npm run build
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
Jenkinsfile 示例
pipeline {
agent { docker { image 'node:20-alpine' } }
stages {
stage('Install') { steps { sh 'npm ci' } }
stage('Lint') { steps { sh 'npm run lint' } }
stage('Test') { steps { sh 'npm run test:unit' } }
stage('Build') { steps { sh 'npm run build' } }
stage('Deploy') {
steps {
sh 'rsync -az --delete dist/ server:/var/www/html/'
}
}
}
post {
failure { // 通知开发团队 }
success { // 部署成功通知 }
}
}
环境管理策略
| 环境 | 用途 | 数据 | 部署方式 |
|---|---|---|---|
| Development | 日常开发调试 | Mock / 本地 | 手动 / Vite dev |
| Staging | 预发布验证 | 脱敏生产数据 | 合并到 release 分支自动部署 |
| Production | 线上用户 | 真实数据 | Tag / 合并到 main 自动部署 |
案例:手动部署导致线上事故
某公司手动 FTP 部署:开发者上传时覆盖了别人的版本,导致线上回退到旧代码,故障持续 2 小时。
引入 CI/CD 后:每次部署可追溯、可回滚,staging 环境先验证再上线。部署时间从 30 分钟缩短到 3 分钟,事故率降为零。
5 性能指标 (Core Web Vitals)
Google 定义的核心 Web 指标,直接影响 SEO 排名和用户体验。
核心指标仪表盘
测量工具
综合评分报告
真实用户指标采集
Performance 面板分析
Core Web Vitals 报告
// 使用 web-vitals 库采集真实指标
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP((metric) => {
// 发送到监控后端
sendToAnalytics({ name: 'LCP', value: metric.value, rating: metric.rating });
});
onINP((metric) => { /* ... */ });
onCLS((metric) => { /* ... */ });
6 性能优化实战
加载优化
Resource Hints 资源提示
<head>
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//cdn.example.com">
<!-- 预连接 (DNS + TCP + TLS) -->
<link rel="preconnect" href="https://api.example.com" crossorigin>
<!-- 预加载关键资源 -->
<link rel="preload" href="/fonts/main.woff2" as="font" crossorigin>
<!-- 空闲时预获取 -->
<link rel="prefetch" href="/js/about-page.js">
</head>
图片优化
<!-- 响应式图片 + WebP + 懒加载 -->
<picture>
<source srcset="hero.webp" type="image/webp">
<source srcset="hero.jpg" type="image/jpeg">
<img
src="hero.jpg"
alt="Hero"
loading="lazy"
width="800" height="400"
srcset="hero-400w.jpg 400w, hero-800w.jpg 800w"
sizes="(max-width: 600px) 400px, 800px"
/>
</picture>
width 和 height 属性可以帮助浏览器预留空间,避免 CLS(布局偏移)。
字体优化
<style>
@font-face {
font-family: 'MainFont';
src: url('/fonts/main-subset.woff2') format('woff2');
font-display: swap; /* 先用系统字体,加载完再替换 */
unicode-range: U+4E00-9FFF; /* 只加载中文字符子集 */
}
</style>
Bundle 分析
// vite.config.ts — 使用 rollup-plugin-visualizer 分析
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({ open: true, gzipSize: true }),
],
});
运行时优化
防抖与节流
搜索框输入、窗口 resize、表单验证
滚动事件、拖拽、射击游戏、scroll 监听
// 防抖实现
function debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// 节流实现
function throttle(fn, interval = 300) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn(...args);
}
};
}
虚拟滚动 (Virtual Scrolling)
// 原理:只渲染可视区域的 DOM,10万条数据也只渲染 20-30 个 DOM 节点
const visibleItems = computed(() => {
const startIdx = Math.floor(scrollTop.value / itemHeight);
const endIdx = startIdx + visibleCount;
return allItems.slice(startIdx, endIdx);
});
requestAnimationFrame 动画
// 不要用 setInterval 做动画,用 rAF 与浏览器刷新率同步
function animate() {
// 更新动画状态
element.style.transform = `translateX(${x}px)`;
if (x < target) {
requestAnimationFrame(animate); // 递归调用,约60fps
}
}
requestAnimationFrame(animate);
Web Worker 重计算
// 主线程
const worker = new Worker('/workers/heavy-compute.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => {
// 拿到结果,不阻塞 UI
updateUI(e.data.result);
};
// heavy-compute.js
self.onmessage = (e) => {
const result = heavyProcessing(e.data);
self.postMessage({ result });
};
渲染优化
关键渲染路径优化
<head>(尽早构建 CSSOM)、JS 放 <body> 底部或加 defer(避免阻塞解析)。
CSS Containment & will-change
/* CSS Containment — 限制浏览器重排/重绘范围 */
.card-list {
contain: layout style paint; /* 隔离渲染边界 */
}
.animated-element {
will-change: transform, opacity; /* 提示浏览器提前优化 */
}
/* 注意:不要滥用 will-change,它本身也有内存开销 */
案例:首屏加载从 6s 优化到 1.5s — 逐步优化过程
7 监控与埋点
错误监控
// 1. Sentry — 最流行的错误监控平台
import * as Sentry from '@sentry/vue';
Sentry.init({
dsn: 'https://xxx@sentry.io/project',
integrations: [Sentry.browserTracingIntegration()],
tracesSampleRate: 0.1, // 采样 10%
release: 'my-app@1.2.3',
});
// 2. 全局错误捕获
// JS 运行时错误
window.onerror = (msg, source, line, col, error) => {
reportError({ type: 'js_error', msg, source, line, col, stack: error?.stack });
return false;
};
// Promise 未捕获异常
window.addEventListener('unhandledrejection', (e) => {
reportError({ type: 'promise_error', reason: e.reason });
});
// 3. Vue 全局错误处理
app.config.errorHandler = (err, vm, info) => {
Sentry.captureException(err);
};
性能监控
// Navigation Timing API — 页面加载各阶段耗时
const [nav] = performance.getEntriesByType('navigation');
const metrics = {
dns: nav.domainLookupEnd - nav.domainLookupStart,
tcp: nav.connectEnd - nav.connectStart,
ttfb: nav.responseStart - nav.requestStart,
download: nav.responseEnd - nav.responseStart,
domParse: nav.domInteractive - nav.responseEnd,
load: nav.loadEventEnd - nav.startTime,
};
// Resource Timing API — 单个资源加载耗时
const resources = performance.getEntriesByType('resource');
resources.forEach(r => {
if (r.duration > 3000) {
reportSlowResource({ name: r.name, duration: r.duration });
}
});
用户行为埋点
// 基础埋点策略
const tracker = {
// 页面浏览
pageView(page) {
send({ event: 'pv', page, timestamp: Date.now() });
},
// 用户点击
click(target, metadata) {
send({ event: 'click', target, ...metadata });
},
// 自定义事件
track(eventName, data) {
send({ event: eventName, ...data });
},
};
// 声明式埋点:Vue 自定义指令
app.directive('track', {
mounted(el, binding) {
el.addEventListener('click', () => {
tracker.track(binding.value);
});
},
});
// <button v-track="'submit_order'">提交订单</button>
案例:上线后白屏了但用户没反馈
某电商大促上线后,部分 iOS 14 用户白屏,但客服没接到投诉(用户直接流失了)。没有监控意味着你永远不知道你不知道什么。
接入 Sentry + 自定义性能监控后:
- 白屏错误在 3 分钟内 触发告警
- 定位到某 polyfill 缺失导致 iOS 14 报错
- 修复上线,全量用户恢复正常
没有监控的系统 = 盲人开坦克。
8 面试题精选 (25道)
点击问题展开答案 ↓
核心解决问题:
• 协作效率:统一的代码规范和流程,减少沟通成本
• 质量保障:自动化测试、lint 检查,减少低级错误
• 开发体验:热更新、自动构建,让开发者聚焦业务
• 可维护性:模块化/组件化架构,方便长期演进
• 部署安全:CI/CD 自动化,可回滚、可追溯
1. 初始化参数:从配置文件和 shell 语句合并参数
2. 开始编译:用参数初始化 Compiler 对象,加载所有 Plugin
3. 确定入口:根据 entry 找到所有入口文件
4. 编译模块:从入口出发,调用 Loader 翻译所有模块,递归查找依赖
5. 完成模块编译:得到每个模块翻译后的内容和依赖关系
6. 输出资源:根据依赖关系组装 Chunk,再转换成单独文件
7. 输出完成:在确定好输出内容后,根据配置的 output 路径和文件名写入文件系统
Loader vs Plugin:Loader 是文件转换器(sass → css),Plugin 是插件(在整个生命周期中钩入事件)。
• Webpack 先打包所有模块再启动 dev server,项目越大越慢
• Vite 利用浏览器原生 ESM,按需编译,启动时只需处理入口文件
• Vite 使用 esbuild(Go 编写)做依赖预构建,比 JS 工具快 10-100 倍
HMR:
• Webpack:修改文件后需要重新构建涉及的模块链
• Vite:只精确重新编译修改的单个模块,浏览器只请求该模块
生产构建:Vite 使用 Rollup,天然支持 Tree Shaking,产出更小。
前提条件:
• 必须使用 ESM(import/export),CommonJS 不行
• 模块不能有副作用(或在 package.json 标记
sideEffects: false)限制:
• 动态导入、反射(如
Object.keys(imported))会导致失效• 有副作用的模块(如 polyfill、CSS 导入)需要标记
• 第三方库如果没有 ESM 版本则无法 Tree Shake
1. 入口分割:配置多个 entry,手动拆分(不推荐,有公共依赖重复)
2. 动态 import():路由级懒加载,最常用
3. splitChunks:Webpack 自动分析,提取公共依赖
最佳实践:
• 路由级别懒加载:
() => import('./views/About.vue')• 第三方库单独打包:vendor chunk
• 公共模块提取:被 2+ chunk 引用的模块提取为 common chunk
• 预加载策略:
import(/* webpackPrefetch: true */ './module')
• 将非 JS 文件转换为 Webpack 能处理的模块
• 执行顺序:从右到左、从下到上
• 例:babel-loader(ES6+→ES5)、css-loader(CSS→JS 模块)、file-loader(文件→URL)
Plugin — 生命周期钩子:
• 监听 Webpack 广播的事件,在合适的时机通过 Webpack 的 API 改变输出结果
• 可以贯穿整个构建周期
• 例:HtmlWebpackPlugin(生成 HTML)、MiniCssExtractPlugin(CSS 抽取为文件)
一句话:Loader 做翻译,Plugin 做增强。
• ESLint:代码质量检查(未使用变量、可能导致 bug 的写法)
• Prettier:代码格式化(缩进、换行、引号、分号)
冲突解决:
• 安装
eslint-config-prettier,它关闭所有和 Prettier 冲突的 ESLint 规则• 在 extends 中放在最后:
extends: [..., 'prettier']• 这样 ESLint 只管质量,Prettier 只管格式,互不干扰
1. Husky:在
.husky/ 目录注册 Git Hooks2. 开发者执行
git commit 时,触发 pre-commit hook3. lint-staged:只对暂存区(staged)的文件运行 lint/format
4. 通过则提交成功,失败则阻止提交
为什么用 lint-staged 而不是全量 lint:
• 大项目全量 lint 可能需要几十秒
• 只检查改动的文件,速度快、反馈及时
• 全量检查放在 CI 中保证全局质量
CD (Continuous Deployment/Delivery):持续部署 — 构建通过后自动部署到服务器
前端为什么需要:
• 质量门禁:PR 合并前必须通过所有检查
• 部署自动化:避免手动 FTP 上传(易出错、不可追溯)
• 环境一致:开发/staging/production 使用相同的构建流程
• 快速回滚:出问题时秒级回退到上一版本
• 可追溯:每次部署对应的 commit、构建日志都有记录
• LCP(Largest Contentful Paint):最大内容绘制 — 页面主要内容加载速度。目标 < 2.5s
• INP(Interaction to Next Paint):交互到下次绘制延迟 — 响应用户交互的速度。目标 < 200ms
• CLS(Cumulative Layout Shift):累积布局偏移 — 页面视觉稳定性。目标 < 0.1
辅助指标:
• FCP:首次内容绘制,目标 < 1.8s
• TTFB:首字节时间,目标 < 800ms
• TTI:可交互时间,目标 < 3.8s
影响:Google 将这些指标纳入 SEO 排名因素。
优化策略:
1. 图片优化:使用 WebP/AVIF、设置尺寸避免重排、用
loading="eager" 对首屏图片2. 关键资源预加载:
<link rel="preload"> LCP 图片/字体3. CSS 优化:内联关键 CSS,避免渲染阻塞
4. JS 延迟:非关键 JS 用
defer 或 async5. CDN:减少 TTFB
6. SSR/SSG:服务端渲染直接输出 HTML
1. 图片无尺寸:设置
width/height 或 aspect-ratio2. 动态注入内容:预留空间(skeleton/占位符),避免已渲染内容被推下去
3. Web 字体闪烁:
font-display: swap 或使用 size-adjust4. 异步加载的广告/嵌入:给容器设定固定高度
5. 动画:使用
transform(合成属性)代替 top/left(触发布局)
例:
<link rel="preload" href="font.woff2" as="font" crossorigin>• prefetch:未来页面可能需要的资源,浏览器空闲时低优先级加载
例:路由懒加载组件的预获取
• preconnect:提前建立连接(DNS + TCP + TLS),不下载资源
例:
<link rel="preconnect" href="https://api.example.com">• dns-prefetch:只预解析 DNS,最轻量
• 场景:搜索框联想、窗口 resize、表单实时校验
节流 (throttle):固定时间间隔执行一次。就像水龙头限制流量。
• 场景:scroll 事件、拖拽、射击游戏点击
手写核心实现:防抖用
setTimeout + clearTimeout;节流用时间戳比较或定时器。
• 计算当前滚动位置对应的可视范围(startIndex ~ endIndex)
• 只渲染这个范围内的 DOM 节点(通常 20-30 个)
• 用一个总高度的容器模拟完整滚动条
• 滚动时动态更新渲染列表
适用场景:数据量 > 1000 条的长列表(聊天记录、日志表格、通讯录)
关键计算:
•
startIndex = Math.floor(scrollTop / itemHeight)•
visibleCount = Math.ceil(containerHeight / itemHeight)• 使用
transform: translateY(offset) 定位
优化策略:
1. CSS 放 head:尽早构建 CSSOM,避免渲染阻塞
2. JS 放 body 底部或 defer:避免阻塞 DOM 解析
3. 内联关键 CSS:首屏渲染所需 CSS 直接嵌入 HTML
4. async/defer:非关键脚本异步加载
5. 减少关键资源数量:合并 CSS、移除无用 CSS
6. 减少关键路径长度:减少关键资源之间的依赖链
1. JS 运行时错误:
window.onerror / window.addEventListener('error')2. Promise 异常:
window.addEventListener('unhandledrejection')3. 资源加载失败:
window.addEventListener('error', ..., true)(捕获阶段)4. 框架错误:Vue
errorHandler / React ErrorBoundary5. 接口错误:拦截 fetch/XHR
Sentry 最佳实践:
• 配置 source map 上传,定位压缩后代码的原始位置
• 设置 release 版本号,区分不同版本的错误
• 配置采样率避免数据量过大
• 添加用户信息、面包屑(操作路径)方便复现
1. 实验室数据(开发阶段):
• Lighthouse CI:集成到 CI 中,每次构建自动跑分
• Chrome DevTools Performance 面板:火焰图分析
2. 真实用户数据(RUM,线上):
• web-vitals 库采集 LCP/INP/CLS
• PerformanceObserver API 采集自定义指标
• Navigation Timing API:DNS/TCP/TTFB/Load 各阶段耗时
• Resource Timing API:单个资源的加载详情
数据上报:用
navigator.sendBeacon 在页面卸载时保证数据不丢失。
• 优点:交互流畅(SPA)
• 缺点:首屏慢、SEO 差
SSR (Server-Side Rendering):服务器执行 JS 渲染 HTML 返回给浏览器。
• 优点:首屏快、SEO 好
• 缺点:服务器压力大、TTFB 增加
SSG (Static Site Generation):构建时预渲染为静态 HTML。
• 优点:最快(CDN 直出)、SEO 好
• 缺点:只适合内容相对固定的页面
选型:内容站 → SSG、动态应用 → SSR+CSR 混合、后台管理 → CSR。
响应式:
srcset + sizes,根据屏幕宽度加载不同尺寸懒加载:
loading="lazy",首屏外图片延迟加载尺寸预留:设置 width/height,避免 CLS
CSS 替代:简单图形用 CSS/SVG 代替图片
CDN 图片处理:阿里云 OSS / Cloudinary 按参数裁剪压缩
渐进式 JPEG:先低清后高清,感知加载更快
1. Webpack dev server 和浏览器之间建立 WebSocket 连接
2. 文件修改 → Webpack 重新编译该模块 → 生成 hot-update.json(manifest)和 hot-update.js(补丁)
3. 通过 WebSocket 通知浏览器:hash 变更
4. 浏览器请求 hot-update.json 获取变更列表
5. 请求 hot-update.js 获取模块代码
6. HMR Runtime 替换旧模块,执行
module.hot.accept 回调关键:只替换变更模块,不刷新页面,保持应用状态。
• 未清除的 setInterval/setTimeout
• 未移除的事件监听器
• 闭包引用了不需要的大对象
• 脱离 DOM 的引用(删除了 DOM 但 JS 还持有引用)
• 全局变量不断累积
排查工具:
• Chrome DevTools → Memory 面板 → Heap Snapshot
• 对比两次快照,找到只增不减的对象
• Performance 面板录制,观察内存是否持续上升
预防:组件销毁时清理副作用(Vue 的
onUnmounted / React 的 useEffect cleanup)。
生产环境策略:
• 不发布到 CDN:source map 只上传到错误监控平台(Sentry),不暴露给用户
• 配置
hidden-source-map:生成 map 但 CSS/JS 中不引用• Sentry 的
@sentry/webpack-plugin 构建时自动上传安全考虑:source map 暴露源代码,绝不应对外公开。使用
devtool: 'hidden-source-map'。
1. 错误监控:
• JS 错误、Promise 异常、资源加载失败、接口错误
• 工具:Sentry、自建上报
2. 性能监控:
• Core Web Vitals (LCP/INP/CLS)
• 页面加载各阶段耗时 (Navigation Timing)
• 接口响应时间
3. 业务监控:
• PV/UV、关键路径转化率
• 用户行为路径(面包屑)
技术要点:
• 采样率控制(避免数据量爆炸)
•
navigator.sendBeacon 保证卸载时上报• 数据聚合 + 报警规则(错误率超过阈值触发告警)
Step 1 — 测量 & 分析:Lighthouse 跑分 → Network 面板看资源 → 确定瓶颈
Step 2 — 快速见效:开启 gzip/brotli、图片压缩、CDN → 通常能减少 50%
Step 3 — JS 优化:Tree Shaking、Code Splitting、懒加载 → 首屏 JS 降到最小
Step 4 — CSS 优化:内联关键 CSS、移除无用 CSS
Step 5 — 资源提示:preload 关键资源、preconnect API 域名
Step 6 — 缓存策略:静态资源强缓存 + 文件名 hash、Service Worker
Step 7 — 渲染优化:骨架屏/SSR 减少 FCP、避免布局偏移
Step 8 — 持续监控:接入 web-vitals 持续采集,回归防止倒退
核心原则:先量化再优化,先低成本再高成本,持续测量。
前端工程化与性能优化 — 面试备战手册 | 全栈知识库