性能指标

15 分钟

为什么性能优化必须先看指标

性能优化最怕只说“感觉快了”。感觉无法复盘,也无法证明收益。真正可落地的优化,应该先把问题变成可度量的指标:页面多久出现内容、主要内容多久可见、用户点击后多久有反馈、页面加载过程中会不会乱跳。

性能指标的价值有三点:

  • 定位问题:判断慢在网络、服务端、资源加载、渲染还是主线程执行。
  • 确定优先级:把用户影响最大的瓶颈放在前面处理。
  • 验证收益:优化前后用同一口径对比,而不是靠主观体验。

面试中回答性能指标,不要只背缩写。更好的结构是:指标衡量什么、用户感知是什么、异常时排查哪里、用什么工具验证

指标可以分成三类

从用户体验角度看,前端性能指标大致分为三类。

类型关注问题典型指标
加载体验页面内容多久出现TTFB、FCP、LCP
交互体验操作后多久有反馈INP、TBT、TTI
视觉稳定性页面会不会突然跳动CLS

Google 提出的 Core Web Vitals 是其中最核心的三个指标:

指标衡量内容好的标准
LCP最大内容绘制,衡量加载体验小于 2.5s
INP交互到下一次绘制,衡量交互响应小于 200ms
CLS累积布局偏移,衡量视觉稳定性小于 0.1

可以把它们理解成用户的三个问题:

  • LCP:主要内容出来了吗?
  • INP:点了之后有反应吗?
  • CLS:页面会不会乱跳?

加载类指标

加载类指标描述页面从发起请求到内容逐步呈现的过程。一个简化时间线如下:

navigationStart
  -> TTFB:收到首字节
  -> FCP:首次内容绘制
  -> LCP:最大内容绘制
  -> load:资源加载完成

TTFB:服务端和网络链路的第一信号

TTFB,全称 Time to First Byte,表示从请求开始到浏览器收到响应第一个字节的时间。

它主要反映:

  • DNS、TCP、TLS、CDN、回源等网络链路耗时。
  • 服务端处理请求的耗时。
  • SSR 页面生成 HTML 的耗时。

如果 TTFB 很高,优先排查:

  • 接口是否慢,数据库查询是否慢。
  • SSR 是否串行等待多个接口。
  • CDN 是否命中,是否跨地域回源。
  • 是否存在重定向链路。

常见优化方向:

  • 服务端接口并行化、缓存热点数据。
  • 静态资源走 CDN,就近访问。
  • 减少不必要的重定向。
  • SSR 页面拆分关键数据和非关键数据。

TTFB 不是完整的用户体验指标。它只能说明“第一个字节来得快不快”,不能说明页面内容是否已经可见。

FCP:用户第一次看到内容

FCP,全称 First Contentful Paint,表示浏览器第一次绘制出文本、图片、SVG、非空白 Canvas 等内容的时间。

FCP 解决的是白屏问题。用户看到第一块内容,至少说明页面不是完全空白。

FCP 异常通常和关键渲染路径有关:

  • HTML 返回太慢。
  • CSS 阻塞渲染。
  • 首屏 JS 过大,解析执行阻塞主线程。
  • 字体或关键资源加载策略不合理。

常见优化方向:

<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin />
<script src="/app.js" defer></script>
  • 关键 CSS 提前加载,非关键 CSS 延后。
  • JS 使用 defer 或拆分首屏包。
  • 首屏内容尽量避免完全依赖客户端 JS 生成。
  • 对字体、首屏图片等关键资源设置合理优先级。

LCP:主要内容何时可见

LCP,全称 Largest Contentful Paint,表示视口内最大内容元素完成渲染的时间。这个元素可能是大图、视频封面、大段文本或块级内容。

LCP 是 Core Web Vitals 之一。它比 FCP 更接近用户对“页面加载好了”的感知。FCP 可能只是出现一个导航栏,LCP 通常代表首屏主体内容已经出现。

LCP 常见问题来源:

问题表现优化方向
服务端慢HTML 很晚返回降低 TTFB、缓存、SSR 并行化
图片过大首屏大图迟迟不显示压缩、WebP/AVIF、尺寸裁剪
加载优先级低LCP 资源排队太久preloadfetchpriority="high"
CSS/JS 阻塞资源到了但不能渲染拆包、延迟非关键脚本
客户端渲染过重HTML 空壳,等 JS 执行SSR/SSG、骨架屏、流式渲染

图片作为 LCP 元素时,常见写法是:

<img
  src="/hero.webp"
  width="1200"
  height="630"
  fetchpriority="high"
  alt="页面主视觉"
/>

这里的 widthheight 不只是为了图片尺寸,也能减少布局偏移,对 CLS 也有帮助。

交互类指标

页面显示出来不代表可用。很多页面看起来加载完成,但点击按钮没有反应,本质是主线程被 JS 任务占住了。

INP:衡量真实交互延迟

INP,全称 Interaction to Next Paint,表示用户触发一次交互后,到浏览器完成下一次绘制之间的时间。它统计页面生命周期内多次交互中较差的一次体验。

INP 是 Core Web Vitals 之一,已经替代 FID 成为核心交互指标。

INP 高通常说明:

  • 事件回调执行太久。
  • 状态更新导致大范围重渲染。
  • 主线程正在执行长任务,事件无法及时处理。
  • 输入、筛选、拖拽、滚动等高频交互没有做控制。

常见优化方向:

input.addEventListener('input', debounce((event) => {
  search(event.target.value)
}, 300))

function debounce(fn, delay) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => fn(...args), delay)
  }
}
  • 高成本计算放到 Web Worker。
  • 长列表使用虚拟滚动。
  • 输入、滚动、窗口变化使用防抖或节流。
  • React/Vue 组件减少无效渲染。
  • 长任务拆成小任务,让主线程有机会响应输入。

TBT:实验室里定位主线程阻塞

TBT,全称 Total Blocking Time,表示 FCP 到 TTI 之间所有长任务超过 50ms 部分的总和。

例如一个任务执行 180ms,其中前 50ms 不算阻塞,剩余 130ms 计入 TBT。

TBT 常用于 Lighthouse 等实验室工具。它和 INP 的关系是:

  • TBT:更适合开发阶段定位加载期间主线程阻塞。
  • INP:更适合线上真实用户交互体验评估。

如果 Lighthouse 报 TBT 高,重点看 Performance 面板里的 Long Task,通常能定位到具体脚本、第三方 SDK 或框架 hydration 阶段。

TTI:页面何时真正可交互

TTI,全称 Time to Interactive,表示页面主要内容可见,并且主线程足够空闲,可以稳定响应用户输入的时间点。

它在理解性能链路时仍然有价值,但现在真实体验评估更推荐关注 INP。因为用户不只关心首次可交互,还关心后续每一次操作是否流畅。

视觉稳定性指标

CLS:页面是否乱跳

CLS,全称 Cumulative Layout Shift,表示页面生命周期内非预期布局偏移的累积分数。

典型问题是:用户准备点击按钮时,图片、广告或异步内容突然加载出来,把按钮挤走,导致误点。

CLS 常见原因:

  • 图片、视频没有设置宽高或宽高比。
  • 广告位、推荐位异步插入但没有预留空间。
  • Web Font 加载前后字体尺寸差异过大。
  • 异步组件加载完成后撑开布局。

常见修复方式:

<img src="/cover.webp" width="800" height="450" alt="封面" />

<div class="ad-slot">广告位</div>
.ad-slot {
  min-height: 250px;
}

@font-face {
  font-family: "Inter";
  src: url("/inter.woff2") format("woff2");
  font-display: swap;
}

CLS 的关键不是“页面不能动”,而是“不能发生用户没有预期的跳动”。用户点击、展开、关闭弹窗导致的布局变化,通常不算坏体验。

Lab 数据和 Field 数据

性能数据有两种口径:实验室数据和现场数据。

类型来源优点局限
Lab 数据Lighthouse、WebPageTest、本地 Performance可复现,适合调试不一定代表真实用户环境
Field 数据RUM、CrUX、线上埋点代表真实体验受设备、网络、地域影响大

正确的工作流是:

Field 数据发现真实问题
  -> Lab 数据复现和定位
  -> 修改后用 Lab 数据快速验证
  -> 上线后用 Field 数据确认收益

如果 Lighthouse 很差,但线上 P75 数据很好,说明问题可能只在模拟环境中明显。反过来,如果本地很快,但线上 Field 数据差,就要考虑低端机、弱网、地域、缓存命中率和第三方脚本差异。

如何采集指标

开发调试可以用:

  • Chrome DevTools Performance 面板。
  • Lighthouse。
  • WebPageTest。
  • PageSpeed Insights。

线上采集可以用 web-vitals

import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals'

function report(metric) {
  navigator.sendBeacon('/api/performance', JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    id: metric.id,
  }))
}

onCLS(report)
onINP(report)
onLCP(report)
onFCP(report)
onTTFB(report)

生产环境里不要只看平均值。性能数据通常长尾明显,更推荐看 P75 或 P95:

  • P75:75% 用户的体验能达到该值,Core Web Vitals 常用口径。
  • P95:观察长尾用户,适合稳定性和极端体验分析。

不同问题该看什么指标

问题现象优先指标辅助指标排查方向
白屏时间长FCPTTFBHTML、CSS、JS 阻塞
首屏主内容慢LCPTTFB、FCP大图、SSR、关键资源优先级
点击没反应INPTBT长任务、事件回调、重渲染
加载时页面乱跳CLSLCP图片尺寸、广告占位、字体
Lighthouse 主线程阻塞TBTINPJS 包体积、第三方脚本
SSR 页面慢TTFBLCP服务端渲染和接口耗时
后台表格卡顿INPLong Task大数据计算、虚拟列表

常见误区

误区一:只看 load 时间

load 表示页面资源加载完成,但用户可能早就看到内容,也可能资源加载完了仍然点不动。它不能代表完整用户体验。

误区二:只看平均值

平均值容易掩盖长尾问题。少量低端设备或弱网用户的体验可能非常差,但平均值看起来还不错。线上性能更应该关注分位数。

误区三:指标越多越好

指标太多会让团队失去焦点。一般页面优先关注 LCP、INP、CLS,再根据问题补充 TTFB、FCP、TBT。

误区四:优化非关键路径

如果问题是 LCP 高,但优化的是非首屏组件包体积,指标可能不会变化。优化必须命中关键路径。

总结

性能指标的核心不是背缩写,而是建立一套判断链路:

  • 加载慢:看 TTFB、FCP、LCP。
  • 交互卡:看 INP、TBT。
  • 页面跳:看 CLS。
  • 开发定位:用 Lab 数据。
  • 线上验收:看 Field 数据和 P75。

面试中可以用一句话收束:Core Web Vitals 里的 LCP、INP、CLS 分别对应加载速度、交互响应和视觉稳定性,优化前先度量,优化后用同一指标口径验证收益。