【performance面试考点】让面试官眼前一亮的 Performance 性能优化
本文面向前端面试,覆盖浏览器渲染机制、重排重绘优化、资源与网络优化、执行与框架层优化、缓存与首屏、度量与工具,并配有可直接落地的代码示例与"高分回答模板"。
一、核心指标与目标(先说度量,展现方法论)
- 指标家族(建议至少说出 5 个并解释场景)
- FP/FCP: 首次/首次内容绘制,用户看到内容的时间。
- LCP: 最大内容绘制,衡量主内容出现速度(目标 ≤ 2.5s)。
- CLS: 累积布局偏移,衡量页面稳定性(目标 ≤ 0.1)。
- TBT/INP/TTI: 交互堵塞/交互响应/可交互时间,衡量主线程繁忙程度与交互体验。
- 目标制定
- 制定"性能预算"(Performance Budget):LCP ≤ 2.5s、CLS ≤ 0.1、首页 JS ≤ 200KB(gzip) 等。
- 建议在 CI/Lighthouse CI 中守护预算,超标直接报警。
二、浏览器渲染原理与重绘重排(高频考点)
- 流程:HTML 解析为 DOM → CSS 解析为 CSSOM → 生成 Render Tree → Layout(重排) → Paint(重绘) → Composite。
- 概念
- 重绘:样式变化但不影响布局,如颜色、背景、阴影。
- 重排:几何属性变化导致布局计算,如大小、位置、显示/隐藏、插入/删除节点。
- 关系:重排一定引发重绘,重绘不一定重排。
高分实践
- 批量合并样式更新,避免布局抖动(Layout Thrashing)
js
// 反例:多次读写交错,易触发多次重排
const el = document.getElementById('el');
el.style.width = '100px';
el.style.height = '100px';
el.style.display = 'block';
// 正例1:使用 cssText 或 class 切换(一次性批量变更)
el.style.cssText = 'width:100px;height:100px;display:block;';
// 或
el.className = 'card card--expanded';
- 使用文档碎片一次性插入,减少回流
js
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
fragment.appendChild(div); // 不触发布局
}
document.body.appendChild(fragment); // 一次性更新
- 读写分离,缓存布局信息
js
const el = document.getElementById('el');
// 反例:循环中每次读取布局 + 写入样式,强制同步布局
for (let i = 0; i < 100; i++) {
el.style.top = el.offsetTop + 1 + 'px';
}
// 正例:先读后写,避免多次强制布局
let top = el.offsetTop;
for (let i = 0; i < 100; i++) {
top += 1;
}
el.style.top = top + 'px';
- 使用
transform/opacity
做动画(只触发合成,不走布局与绘制)
js
// 反例:left/top 会引发布局
el.style.left = '100px';
// 正例:GPU 友好的属性
el.style.transform = 'translateX(100px)'; // 或配合 transition/WAAPI
el.style.opacity = '0.8';
- 在动画/频繁更新中使用
requestAnimationFrame
,与浏览器帧同步 - 大量 DOM 操作前可"下线"节点(合理使用
display:none
或离屏容器)后批量更新再上线 - 避免频繁读取以下属性导致强制同步布局:
offsetTop/Left/Width/Height
、getComputedStyle
、scrollTop
等;如需读取,批量读取后再统一写入
三、资源加载优化(提速"下载+执行")
- 代码分割与路由懒加载(减少首屏体积)
- Webpack/Rollup/Vite 动态导入:
import('...')
- React 路由懒加载:
React.lazy + Suspense
- Webpack/Rollup/Vite 动态导入:
- 资源提示(Resource Hints)
preload
:当前页面"关键资源",立刻加载
<link rel="preload" as="style" href="/critical.css">
prefetch
:未来可能用到的资源,空闲加载
<link rel="prefetch" href="/next-page.js">
preconnect
:提前建立连接(DNS、TLS、TCP)
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
dns-prefetch
:提前 DNS 解析
<link rel="dns-prefetch" href="//cdn.example.com">
- 脚本加载策略
defer
:并行下载,按文档顺序执行,DOMContentLoaded 前执行async
:并行下载,下载完成立刻执行(顺序不确定)type="module"
:天然defer
行为,支持按需与 Tree-shaking
- 图片优化
- 新格式:优先 AVIF/WebP,并提供回退
- 响应式:
srcset
+sizes
,按视口和 DPR 提供最佳图 - 懒加载:
<img loading="lazy">
;解码:decoding="async"
;优先级:fetchpriority="high|low"
- 雪碧图/IconFont 已不再主流,优先 SVG symbol 或 Icon 组件
- 样式优化
- Critical CSS 内联,非关键样式
media
/preload
延后 - 减少阻塞渲染资源(阻塞 CSS/同步 JS)
- Critical CSS 内联,非关键样式
四、JS 执行与主线程优化(降低卡顿)
- 减少长任务(>50ms),拆分任务,利用切片
- 防抖/节流:输入、滚动、窗口变更等高频事件
js
const throttle = (fn, wait) => {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last > wait) {
last = now;
fn(...args);
}
};
};
window.addEventListener('scroll', throttle(handleScroll, 100));
- requestAnimationFrame :动画写入;requestIdleCallback:空闲期任务(兜底逻辑)
- Web Workers :把密集计算从主线程挪走;极致可考虑 WASM
- 大列表优化:虚拟列表(windowing)、分页、占位骨架
五、框架层优化(React/前端框架)
- React 重渲染治理
memo/useMemo/useCallback
控制子组件重渲染- 合理设置
key
,避免 Diff 错误带来的 DOM 抖动 - 避免在 render 中创建新对象/函数;减少 Context 滥用带来的联动渲染
- React 18 并发特性 +
startTransition
提升交互流畅度
- 构建优化
- 按需引入组件库(如 shadcn-ui)与 Tree-shaking
- 生产构建压缩:Terser/ESBuild,移除 dead code、console、debug
- 预编译依赖(Vite deps prebundle),SSR 构建分离
六、缓存策略(命中缓存=最快)
- 强缓存
Cache-Control: max-age=31536000, immutable
- 避免使用
Expires
(受客户端时间影响)
- 协商缓存
Last-Modified/If-Modified-Since
ETag/If-None-Match
(更精准,文件指纹强制更新)
- 版本化与回滚
- 文件名指纹(hash)+ CDN 缓存:更新即失效
- 存储
localStorage/sessionStorage
存读小数据- IndexedDB/Cache Storage(配合 Service Worker 缓存接口/静态资源)
- Service Worker/PWA
- 离线缓存、预缓存关键静态资源、离线落地页
七、网络与后端协同(系统视角)
- CDN:就近访问、缓存静态资源、智能压缩(Brotli/Gzip)
- HTTP/2/3:多路复用、头部压缩、Server Push(H2 已不鼓励滥用 Push)
- 现代场景中不再建议为"多路复用"而"域名分片"(HTTP/1.1 时代产物)
- 压缩:Brotli > Gzip;开启静态资源压缩与按类型压缩
- 预渲染与直出
- SSR/SSG/ISR:首屏直出、SEO 友好、结合边缘节点(Edge Runtime)
- Critical Rendering Path 精简:Critical CSS、延迟 Hydration、Streaming SSR(React 18/Suspense)
- 图片与数据预取(结合路由/埋点预测)
八、首屏优化(大厂最关注)
- SSR/SSG/ISR 与流式渲染:组件在服务端渲染,客户端接管更少的 JS
- 骨架屏:避免白屏与布局跳动,降低 CLS
- 渲染顺序控制
- 首屏关键模块优先,低优先级资源
prefetch
/lazy
- 组件"按需 Hydration"(岛屿架构/Partial/延迟激活)
- 首屏关键模块优先,低优先级资源
- 数据就近:边缘缓存 + CDN KV/Edge DB,降低 RTT
九、易被忽视的"锦上添花"
- 使用
will-change
/contain
控制合成与布局影响范围(谨慎使用,避免内存增加) - 字体优化:
font-display: swap
、子集化字体、preload
字体并设置crossorigin
- 图片占位(LQIP/BlurHash):避免 CLS
- 监控与回归
- RUM(真实用户监控):收集 LCP/CLS/INP 与地理、设备维度
- A/B 性能实验:验证优化是否真正有效
十、常见面试题"高分回答模板"
-
问:如何系统性优化首屏?
- 答:明确指标(LCP/CLS/TBT),落预算;SSR/SSG 输出 HTML,Critical CSS 内联;主 JS 分割 + 路由懒加载;关键图片 WebP/AVIF +
preload
首图;脚本defer/module
;CDN + Brotli;通过 RUM 监控与 A/B 验证优化闭环。
- 答:明确指标(LCP/CLS/TBT),落预算;SSR/SSG 输出 HTML,Critical CSS 内联;主 JS 分割 + 路由懒加载;关键图片 WebP/AVIF +
-
问:如何减少重排重绘?
- 答:批量 DOM 更新(文档碎片/class 切换)、读写分离缓存布局、动画用 transform/opacity + rAF、避免强制同步布局(如频繁读取
offset*
)、必要时下线节点后批量操作。
- 答:批量 DOM 更新(文档碎片/class 切换)、读写分离缓存布局、动画用 transform/opacity + rAF、避免强制同步布局(如频繁读取
-
问:
async
、defer
、module
区别?- 答:
async
并行下载立即执行(顺序不定);defer
并行下载按顺序、在 DOMContentLoaded 前执行;module
天生defer
,支持依赖与 Tree-shaking。
- 答:
-
问:HTTP 缓存怎么设计?
- 答:静态资源走强缓存(指纹 +
Cache-Control: max-age=31536000, immutable
),变更即换名;HTML 不缓存或短缓存 + 协商缓存;配合 ETag 精准回源;CDN 边缘缓存命中。
- 答:静态资源走强缓存(指纹 +
-
问:长列表如何优化?
- 答:虚拟列表(windowing)、分页与分块渲染、占位骨架、避免复杂 item 渲染(memo)、滚动事件节流。
十一、示例清单(提及即加分)
- 批量样式变更优先
className
/cssText
- 大量插入用
DocumentFragment
- 读写分离、缓存
offsetTop
等布局信息 - 动画优先
transform/opacity
+rAF
- 代码分割、路由懒加载、组件按需
- 资源提示:
preload/prefetch/preconnect/dns-prefetch
- 图片:
AVIF/WebP + srcset/sizes + loading="lazy" + fetchpriority
- 字体:
preload
+font-display: swap
- 缓存:强缓存 + 协商缓存 + 文件指纹 + Service Worker
- 网络:CDN + Brotli + HTTP/2/3,避免域名分片
- 首屏:SSR/SSG/ISR + Critical CSS + 流式渲染 + 骨架屏
- 监控:Lighthouse/Chrome DevTools/WebPageTest + RUM