Files

4.0 KiB
Raw Permalink Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week, order
id title type domain stack tags level prerequisites est_hours related week order
frontend.fe-testing-perf.web-vitals 前端性能:Core Web Vitals(LCP / INP / CLS)、长任务和优化清单 lesson frontend
fe-testing-perf
性能优化
Core Web Vitals
LCP
INP
CLS
长任务
监控
intermediate
frontend.ssr.rendering-modes
2.5
frontend.fe-testing-perf.scenario.perf-regression
frontend.webpack.scenario.build-speed
7
2

前端性能:Core Web Vitals(LCP / INP / CLS)、长任务和优化清单

指标定义和阈值以官方文档为准:https://web.dev/articles/vitals 。

解释说明

Core Web Vitals

指标 含义 “良好”阈值(按 75 分位统计) 常见原因
LCP(最大内容绘制) 视口中最大的图片 / 文本块渲染完成的时间 ≤ 2.5 秒 服务器慢、渲染阻塞资源、首屏大图没优化、客户端渲染
INP(交互到下一次绘制) 用户交互到页面视觉响应的延迟,取整个访问期间较差的值 ≤ 200 毫秒 主线程长任务、事件处理太重、大范围重新渲染
CLS(累计布局偏移) 页面元素意外移动的程度 ≤ 0.1 图片 / 广告没有预留尺寸、字体切换、动态插入内容

INP 在 2024 年取代了 FID。

实验室数据和真实用户数据

  • 实验室数据:Lighthouse、浏览器性能面板,可复现,适合调试和 CI 回归。
  • 真实用户数据(RUM):在线上用 web-vitals 库采集上报,反映真实设备和网络;以 75 分位判断。

长任务

主线程上超过 50 毫秒的任务。期间无法响应点击,导致 INP 变差。办法:把计算切块并让出主线程(scheduler.yield() 或 setTimeout)、移到 Web Worker、减少重新渲染的范围、虚拟列表。

优化清单

  • LCP:SSR / SSG、关键资源预加载(<link rel="preload">)、图片压缩和现代格式、首屏图不要懒加载、CDN。
  • INP:拆分长任务、防抖节流、React 的 useTransition / useDeferredValue、虚拟列表。
  • CLS:图片和视频写宽高或 aspect-ratio、为动态内容预留空间、字体 font-display。
  • 通用:减少 JS 体积(懒加载、按需引入)、HTTP 缓存、压缩。

案例

AI 对话页面,回答很长时流式追加 Markdown 并实时高亮代码,输入框打字卡顿(INP 变差):每收到一个 token 就重新解析整段 Markdown。改为按时间批量合并 token(如每 50ms 渲染一次)、只重新解析最后一个段落、代码高亮放到 Worker。

代码示例

把大量处理切块、让出主线程(examples/search-box/src/chunked.ts,node src/chunked.ts 自检通过):

export async function processInChunks<T, R>(items: readonly T[], fn: (item: T) => R,
  { budgetMs = 8, yieldFn = defaultYield, now = () => performance.now() } = {}) {
  const results: R[] = [];
  let start = now();
  for (const item of items) {
    results.push(fn(item));
    if (now() - start >= budgetMs) {     // 本块用完预算,让浏览器先处理交互和渲染
      await yieldFn();
      start = now();
    }
  }
  return results;
}

线上采集(示意,API 以 web-vitals 库文档为准):

import { onCLS, onINP, onLCP } from "web-vitals";

function report(metric: { name: string; value: number; id: string }) {
  navigator.sendBeacon("/api/rum", JSON.stringify({ ...metric, page: location.pathname }));
}
onLCP(report);
onINP(report);
onCLS(report);

易错点

  • 只看本地 Lighthouse 分数,不看真实用户的数据。
  • 把首屏大图也懒加载了,LCP 反而变差。
  • 用平均值而不是分位数衡量。
  • 优化完没有监控和预算,几个版本后退化。

面试常问

  • 说说 LCP、INP、CLS 和各自的优化方法。
  • 页面交互卡顿怎么排查?
  • 你做过的性能优化,数据是多少?怎么测的?