跳到主要内容

前端的性能优化有哪些?

面试速答(30 秒版 TL;DR)

前端性能优化不要背零散技巧,面试里更好的表达方式是按链路拆:

  1. 网络与传输层:减少请求耗时和传输体积,比如 CDN、HTTP 缓存、Gzip/Brotli、DNS 预解析、preconnect
  2. 资源加载层:让关键资源更早到,非关键资源更晚到,比如代码分割、懒加载、preload、图片懒加载、字体优化。
  3. 渲染层:减少主线程压力,缩短从 DOM/CSSOM 到 Paint/Composite 的时间,比如关键 CSS、减少阻塞 JS、避免频繁回流重绘、动画优先 transform/opacity
  4. 运行时层:减少无效计算和无效渲染,比如防抖节流、虚拟列表、Web Worker、组件更新边界优化。
  5. 工程与监控层:做体积治理、性能预算、指标采集、线上监控,避免“优化一次,回退三次”。

面试结论可以压成一句话:

前端性能优化的本质,就是让“关键内容更早出现、交互更快响应、资源更少浪费、性能退化可被持续发现”。


先建立心智模型:性能到底在优化什么?

前端性能不是单一指标,它通常至少包含四类目标:

目标用户感知常见指标
加载快页面尽快显示内容FCP、LCP、TTFB
交互快点击后尽快响应INP、TBT
渲染稳页面不乱跳、不闪烁CLS
运行省不容易卡顿、发热、爆内存FPS、内存占用、长任务数量

现代项目常用 Core Web Vitals 作为统一口径:

  • LCP(Largest Contentful Paint):衡量主要内容出现得够不够快。
  • INP(Interaction to Next Paint):衡量用户交互后的整体响应延迟。
  • CLS(Cumulative Layout Shift):衡量页面视觉稳定性。
面试表达建议

如果面试官问“前端性能优化有哪些”,最好先讲“我会先看指标,再决定优化抓手”,这比纯列点更像真实工程实践。


一个总览图:从请求到可交互,哪里都可能慢

所以优化时也应该按链路思考,而不是只盯着“打包体积”。


一、网络与传输层优化

1. CDN 就近分发

  • 静态资源放 CDN,缩短传输距离,降低跨地域访问延迟。
  • 热点资源由边缘节点缓存,源站压力更低。
  • 对图片、字体、JS/CSS 这类静态资源收益最明显。

常见追问:

  • 为什么 CDN 能提升性能? 因为核心不是“带宽更大”,而是“用户到资源的物理距离更近,命中缓存的概率更高”。

2. 合理使用缓存

缓存通常分两层:

类型典型响应头作用
强缓存Cache-Control: max-age=31536000, immutable在有效期内直接使用本地副本,不发请求
协商缓存ETag / If-None-MatchLast-Modified / If-Modified-Since资源可能变化时,用较低成本确认是否更新

实践建议:

  • 带 hash 的静态资源走长期强缓存。
  • HTML 通常不做长期强缓存,因为它需要引用最新资源地址。
  • 接口缓存要根据业务时效性设计,不能一刀切。

3. 启用压缩

  • 文本资源优先开启 Brotli,其次是 Gzip
  • HTML、CSS、JS、JSON、SVG 都适合压缩。
  • 图片和视频这类已经压缩过的二进制资源,重复压缩收益通常不大。

4. 降低连接建立成本

可用的资源提示:

<link rel="dns-prefetch" href="//static.example.com" />
<link rel="preconnect" href="https://static.example.com" crossorigin />
  • dns-prefetch:提前做 DNS 解析。
  • preconnect:提前建立 DNS、TCP、TLS 连接,适合确定会访问的关键域名。

5. 减少请求数量,但不要机械合并

过去常讲“合并请求越少越好”,但在 HTTP/2/HTTP/3 下要更谨慎:

  • 小文件过多会有请求管理成本。
  • 但无脑合并成超大 bundle,也会拖慢首屏和缓存复用。
  • 现代实践更强调:按业务边界拆分,让当前页面只下载当前需要的代码

二、资源加载层优化

1. 代码分割(Code Splitting)

目标:减少首屏必须下载和执行的 JS

常见拆分维度:

  • 路由级拆分
  • 组件级异步加载
  • 第三方库按需引入
  • 大型编辑器、图表库延迟加载
const ChartPanel = React.lazy(() => import('./ChartPanel'))

核心收益:

  • 降低首屏 bundle 体积
  • 缩短下载、解析、执行时间
  • 提高缓存复用率

2. Tree Shaking 与依赖治理

  • 移除未使用代码,避免“引了但没用”。
  • 避免引入过重依赖,例如日期库、富文本库、图表库、工具库的全量版本。
  • 优先选择支持 ESM、按需引入、sideEffects 标注清晰的库。

典型表达:

  • lodash 全量引入往往不如按函数引入。
  • moment 往往比 dayjsdate-fns 更重。

3. 懒加载非关键资源

常见对象:

  • 首屏以下图片
  • 弹窗、抽屉、编辑器
  • 评论区、推荐区、埋点脚本
  • 地图、图表、客服 SDK

图片原生懒加载:

<img src="/images/demo.webp" loading="lazy" alt="示例图片" />

4. 预加载关键资源

区分两个概念:

技术含义适用场景
preload当前页面马上会用到,优先加载首屏字体、关键图片、关键脚本、关键 CSS
prefetch未来可能会用到,空闲时预取下一跳页面资源、低优先级模块

示例:

<link rel="preload" href="/fonts/app.woff2" as="font" type="font/woff2" crossorigin />
<link rel="prefetch" href="/assets/settings.chunk.js" />
容易答错的点

preload 不是越多越好。它会抢占带宽,如果把非关键资源也都设成高优先级,反而可能拖慢真正的关键资源。

5. 脚本加载策略

<script src="/app.js" defer></script>
<script src="/analytics.js" async></script>
  • defer:下载不阻塞 HTML 解析,等 DOM 解析完成后按顺序执行,适合主业务脚本。
  • async:下载不阻塞解析,但下载完成就立即执行,顺序不保证,适合独立脚本。

三、页面渲染层优化

1. 缩短关键渲染路径

浏览器首屏渲染大致要经过:

  1. 解析 HTML 构建 DOM
  2. 解析 CSS 构建 CSSOM
  3. 合成 Render Tree
  4. Layout
  5. Paint
  6. Composite

所以常见优化手段包括:

  • 减少阻塞渲染的 CSS/JS
  • 关键 CSS 优先
  • 非关键资源延迟
  • 减少主线程长任务

2. 关键 CSS(Critical CSS)

把首屏必须的样式优先提供,避免“页面结构到了,但样式没到”。

<style>
.hero {
min-height: 320px;
padding: 24px;
}
.title {
font-size: 32px;
font-weight: 700;
}
</style>

注意:

  • 只内联首屏必须样式。
  • 非关键 CSS 继续走外链,保证缓存粒度。
  • 内联过多会增大 HTML 体积,不是越多越好。

3. 降低回流(Layout)和重绘(Paint)

先分清三个概念:

阶段含义成本
Layout计算元素几何位置和尺寸
Paint把像素绘制出来中到高
Composite图层合成相对低

实践建议:

  • 动画优先改 transformopacity
  • 避免频繁修改 widthheighttopleft
  • 批量读写 DOM,避免布局抖动(layout thrashing)
  • 减少大面积阴影、滤镜、模糊效果

4. 控制 DOM 规模和层级

  • DOM 节点过多会提高样式计算、布局、绘制成本。
  • DOM 层级过深会增加渲染和维护复杂度。
  • 长列表、树结构、表格类场景要特别关注节点数量。

典型方案:

  • 分页
  • 虚拟列表
  • 按需展开
  • 折叠非关键区域

相关专题可以继续看本站已有文档:虚拟列表原理

5. 减少布局偏移(CLS)

CLS 高通常不是“慢”,而是“页面乱跳”。

常见原因:

  • 图片、广告、iframe 没有预留尺寸
  • 字体切换导致文字重新排版
  • 异步插入内容把已有内容顶下去
  • 骨架屏和真实内容尺寸不一致

优化建议:

  • 给图片和媒体元素明确宽高或比例盒子
  • 字体使用 font-display
  • 异步内容预留占位空间
  • 骨架屏尽量贴近真实布局

四、静态资源优化

1. 图片优化

图片通常是页面里最重的资源类型,优化优先级很高。

核心策略:

  • 选择合适格式:照片优先 WebP / AVIF,图标优先 SVG
  • 控制分辨率,避免“大图小用”
  • 生成响应式图片
  • 首屏图重点关注 LCP
  • 非首屏图懒加载

响应式图片示例:

<img
src="/images/card-800.webp"
srcset="/images/card-400.webp 400w, /images/card-800.webp 800w, /images/card-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="商品图片"
/>

更完整的话题可看:图片压缩与优化全指南

2. 字体优化

字体很容易被忽视,但它既影响首屏,也影响 CLS。

建议:

  • 优先用 woff2
  • 子集化字体,只保留需要的字符集
  • 首屏关键字体可以 preload
  • font-display: swap 或按场景选择策略
@font-face {
font-family: 'AppFont';
src: url('/fonts/app.woff2') format('woff2');
font-display: swap;
}

3. CSS 优化

CSS 优化主要有两类:

  1. 交付性能:减小体积、减少阻塞、合理拆分。
  2. 运行时性能:减少样式重算、重排、重绘。

关键点:

  • 移除无用 CSS
  • 避免 @import
  • 按页面或组件拆分样式
  • 减少高成本视觉效果
  • 长内容场景可评估 content-visibility: auto

五、JavaScript 执行层优化

1. 减少主线程长任务

即使资源已经下载完成,也可能因为 JS 执行太重而导致首屏晚、交互卡。

常见重任务来源:

  • 大型 bundle 初始化
  • JSON 大对象解析
  • 同步计算过多
  • 一次性渲染大量节点
  • 第三方 SDK 初始化太重

建议:

  • 大任务拆小
  • 延迟非关键逻辑
  • 避免首屏做不必要计算
  • 将重计算迁移到 Web Worker

2. 防抖和节流

适用场景:

  • 输入搜索
  • 滚动监听
  • 窗口 resize
  • 鼠标移动

核心价值不是“语法技巧”,而是降低高频事件触发的计算次数

3. Web Worker

如果计算本身无法避免,就把它挪出主线程。

适用场景:

  • 大数据排序、筛选、聚合
  • 富文本转换
  • 图像处理
  • 加密解密
  • 大文件切片
const worker = new Worker(new URL('./worker.js', import.meta.url))
worker.postMessage({ type: 'calculate', payload: largeData })
worker.onmessage = (event) => {
console.log('worker result', event.data)
}

4. 避免内存泄漏

性能不只是“快不快”,还包括“稳不稳”。

常见内存泄漏来源:

  • 未移除的事件监听
  • 未清理的定时器
  • 闭包长期持有大对象
  • 脱离文档树但仍被引用的 DOM
  • 缓存无限增长

如果页面“越用越卡”,往往要查内存而不是只查网络。


六、框架层优化(React / Vue / 通用组件化场景)

1. 减少无效渲染

通用原则:

  • 状态尽量局部化,不要一点小状态导致整页更新。
  • 组件边界清晰,让更新范围尽量小。
  • 大列表、大表格、大表单避免全量重渲染。

2. 列表渲染优化

常见手段:

  • 稳定的 key
  • 分页
  • 虚拟列表
  • 惰性挂载不可见区域

3. 延迟非关键组件挂载

例如:

  • 不在首屏的 Tab 面板
  • 很晚才打开的弹窗
  • 后台图表分析模块
  • 编辑器、代码高亮器

4. 谨慎做“缓存”

很多人会把性能优化等同于“缓存所有东西”,但缓存也有成本:

  • 占内存
  • 增复杂度
  • 可能带来脏数据
  • 可能导致难以定位的显示错误

所以要强调:缓存是用空间换时间,但必须证明值得换


七、数据获取与接口层优化

1. 减少无效请求

  • 合并重复请求
  • 做请求去重
  • 组件卸载时取消不再需要的请求
  • 避免瀑布流请求链过深

2. 做好数据缓存

常见场景:

  • 列表页返回再进入
  • 用户信息、权限信息、字典数据
  • 不频繁变化的配置数据

缓存层次可以是:

  • 内存缓存
  • 浏览器缓存
  • Service Worker 缓存
  • IndexedDB / localStorage(按场景评估)

3. 优化接口返回

  • 只返回当前页面需要的字段
  • 避免一次返回超大列表
  • 分页、游标、增量加载
  • 压缩响应体

前端性能问题不总是前端代码造成的,接口设计也经常是瓶颈。


八、首屏与白屏专项优化

这是最常被单独追问的部分。

高收益手段通常包括:

  1. 缩短 TTFB
  2. 使用 CDN
  3. 压缩和缓存静态资源
  4. 关键 CSS 优先
  5. defer 业务脚本,减少阻塞
  6. 首屏代码分割
  7. 骨架屏
  8. 图片优化和懒加载
  9. SSR / SSG / 预渲染
  10. 减少第三方脚本干扰

相关专题可以继续看:白屏优化

先按根因分组,再落到对应优化手段。


九、性能监控与验证

1. 先测量,再优化

不要靠“感觉快了”。

常见工具:

  • Chrome DevTools Performance
  • Lighthouse
  • WebPageTest
  • Coverage
  • Performance 面板中的长任务、Layout、Paint 分析

2. 线上采集核心指标

可以通过 PerformanceObserver 采集部分指标:

const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.startTime)
}
})

observer.observe({ type: 'largest-contentful-paint', buffered: true })

说明:

  • FCP、LCP、CLS、INP 这类指标更适合进入前端监控平台长期观察。
  • 最好分端、分地区、分网络、分页面类型统计。

3. 建立性能预算

常见预算示例:

  • 首屏 JS 不超过 200 KB gzip
  • LCP 目标小于 2.5s
  • CLS 小于 0.1
  • 单个页面第三方脚本数量受控

没有预算,性能优化通常很难持续。


十、推荐的优化顺序

这是面试里很加分的一段,因为它体现的是工程优先级。

  1. 先看指标:慢的是加载、交互,还是稳定性?
  2. 先抓大头:图片、首屏 JS、TTFB、第三方脚本、长任务。
  3. 先做高 ROI 项:压缩、缓存、拆包、懒加载、图片格式优化。
  4. 再做专项优化:虚拟列表、Worker、SSR、骨架屏、字体治理。
  5. 最后做持续治理:监控、预算、CI 审计、依赖治理。

一个很实用的表达方式:

我一般不会一上来就做微优化,而是先确认瓶颈在网络、资源、渲染还是运行时,再优先处理影响面最大、收益最稳定的部分。


典型题 & 标准答法

Q1:前端性能优化有哪些?你怎么分类回答?

答法建议:

  • 我会按链路回答,而不是按零散技巧回答。
  • 第一类是网络与缓存,比如 CDN、缓存、压缩、预连接。
  • 第二类是资源加载,比如代码分割、懒加载、图片优化、字体优化、preload
  • 第三类是渲染优化,比如关键 CSS、减少阻塞 JS、减少回流重绘、控制 DOM 数量。
  • 第四类是运行时优化,比如防抖节流、虚拟列表、Web Worker、减少无效渲染。
  • 第五类是工程治理,比如体积分析、性能预算、线上监控。

Q2:为什么说减少首屏 JS 体积往往收益很高?

因为 JS 不只是“下载成本”,还有解析、编译、执行成本。尤其在中低端设备上,执行成本常常比下载更痛。首屏 JS 太大,会拖慢 FCP、LCP,也会拉高 TBT 和 INP。

Q3:asyncdeferpreloadprefetch 分别适合什么场景?

答法要点:

  • async:独立脚本,执行顺序不重要。
  • defer:主业务脚本,保证顺序,避免阻塞 HTML 解析。
  • preload:当前页面马上要用的关键资源。
  • prefetch:未来大概率会用,但不是当前关键路径资源。

Q4:只做打包优化够吗?

不够。前端性能至少还涉及:

  • 网络连接和 TTFB
  • 图片和字体
  • 渲染链路
  • 主线程长任务
  • 运行时内存
  • 第三方脚本
  • 线上监控

只盯打包体积,很容易漏掉真正的大头。

Q5:性能优化应该怎么验证?

答法要点:

  • 本地用 Lighthouse、DevTools、Coverage、Performance 面板定位。
  • 线上采集 Core Web Vitals 和长任务指标。
  • 做 A/B 对比,确认优化前后指标变化。
  • 关注不同设备、不同网络环境下的结果,而不是只看自己的开发机。

常见误区

  • 误区 1:把所有资源都 preload。 结果可能是带宽竞争,关键资源反而更慢。

  • 误区 2:只看包大小,不看执行时间。 JS 慢很多时候不是下载慢,而是解析执行慢。

  • 误区 3:只做实验室数据,不看真实用户数据。 本地跑得快,不代表弱网和低端机也快。

  • 误区 4:把缓存当万能药。 缓存会引入失效、容量、脏数据问题。

  • 误区 5:为了性能牺牲可维护性。 某些“极限优化”会让工程复杂度暴涨,长期未必划算。


速记要点(适合背诵)

可以把前端性能优化背成这 8 组关键词:

  1. 连得快:DNS、TCP、TLS、CDN、预连接
  2. 传得少:压缩、缓存、精简资源
  3. 下得准:代码分割、懒加载、按需加载
  4. 来得早:关键 CSS、preload、首屏优先
  5. 渲得轻:少 DOM、少回流、少重绘
  6. 算得省:防抖节流、Worker、减少长任务
  7. 更稳定:预留尺寸、控制 CLS、清理内存
  8. 可治理:预算、监控、分析、持续优化

如果面试官继续追问,你可以沿着这 8 组逐步展开。