Web Vitals:实验室数据 vs 现场数据
Lighthouse 跑分高但用户还觉得慢?实验室(模拟)和现场(真实用户)差距可能很大。本文讲清两种数据的区别、为什么以现场为准(真实设备/网络/行为)、怎么收集现场数据(CrUX/web-vitals 库/RUM),以及两种数据各自的用途。
本文要点
- 主题聚焦:Next.js、React
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
做过性能优化的同学多半遇到过这个场景:本地跑 Lighthouse,分数 98,各种指标一片绿,自我感觉良好。结果上线一周,客诉"页面卡""打不开",老板截图质问。你看监控,发现真实用户的 LCP 中位数是你本地测的 3 倍。
这不是你测错了,是你测的"不是同一件事"。
Web Vitals 有两套数据来源:实验室数据(Lab Data) 和 现场数据(Field Data)。它们衡量的是完全不同的东西,忽视这个差异,你的性能优化可能一直在原地打转。
一、实验室数据:在干净环境里跑出来的"理想值"
实验室数据,顾名思义,是在一个受控的、模拟的环境里采集的。代表工具是 Lighthouse(Chrome DevTools 内置、PageSpeed Insights、CI 里跑的那个)和 WebPageTest。
它的工作方式大致是这样:
- 一台配置确定的机器(通常是机房里一台规格固定的服务器,或者 CI runner);
- 模拟一个网络节流 profile(比如 4G = 9Mbps 带宽 / 170ms RTT);
- 模拟 CPU 降速(4× 节流);
- 用一个全新的、空缓存的浏览器会话;
- 加载页面、执行一些预设交互(或者干脆不交互,只测加载);
- 跑完给你一整套指标 + 评分。
它的核心优势是 可复现:同一个 commit、同一套配置,你今天跑和明天跑,数字应该几乎一样。这意味着你可以拿它做回归对比——"这次改动让 LCP 从 2.1s 降到 1.8s 吗?",答案明确。
但它的致命弱点也正是那个"受控环境"。模拟环境永远不是真实环境。
二、现场数据:真实用户身上采集的"真相"
现场数据,又叫 Field Data 或 RUM(Real User Monitoring),是从真实访问你网站的用户浏览器里上报回来的。
每一个真实用户访问时,他的浏览器会实际测量 LCP、INP、CLS 这些指标,然后通过一段 JS(Beacon / fetch)把这些数字连同一些维度(设备类型、网络条件、页面路径)发回你的服务器,或者发到 Google 那边。
现场数据反映的是真实分布:你的用户里,有人用着 iPhone 15 Pro + 家用千兆光纤,也有人用着三年前的安卓千元机 + 信号飘忽的 4G,还有人开着 50 个标签页、装了 8 个广告拦截插件、后台还在下载系统更新。每一个用户的体验都是一份数据点,汇总起来才是你的网站"在野外"的真实表现。
实验室数据告诉你"这个页面能有多快";现场数据告诉你"这个页面实际有多快"。
三、为什么两者差距能大得离谱
很多团队第一次认真对比实验室和现场数据时都会震惊——差距常常是 2-5 倍。原因不神秘:
1. 用户的设备比你的 Mac 慢得多。 你在 M2 MacBook Pro 上跑 Lighthouse,CPU 节流 4× 之后仍然比一台中端安卓机的 CPU 快。真实用户的低端设备,RUN 一个 React hydration 可能就要 1.5s,而你本地只要 200ms。
2. 用户的网络比你的光纤慢得多。 模拟的 4G 是一个"理想 4G"——稳定 9Mbps、170ms RTT。真实世界的 4G 可能瞬间掉到 1Mbps、RTT 飙到 400ms,地铁里还会断流重连。你 50ms 拿到的首字节,用户可能要等 800ms。
3. 用户的浏览器状态你模拟不了。 缓存命中情况、Service Worker 拦截、后台标签的资源争抢、扩展插件注入的脚本、屏幕实际尺寸和 DPR、电池省电模式降频……这些 Lighthouse 一个都模拟不了。
4. 用户的行为你预测不到。 Lighthouse 测的是"加载完就完事",但真实用户会立刻滚动、马上点按钮、在页面还没 ready 时就疯狂交互——这些都会触发 INP(交互延迟),而实验室里你压根没测。
所以一个铁律:如果你的优化只在实验室数据上变好、现场数据没动,那很可能你优化的是一个"模拟问题",而不是用户的真实问题。
四、Google 排名用的是现场数据,不是 Lighthouse 分
这一点很多人都搞错了。PageSpeed Insights 页面上你会看到两个 tab:实验室数据 和 现场数据。那个现场数据来自 CrUX(Chrome User Experience Report)——Google 从真实 Chrome 用户那里采集的、按域名聚合的性能数据。
Google 搜索排名用的"页面体验信号"(Page Experience signal),看的是 CrUX 现场数据,不是 Lighthouse 分。 你 Lighthouse 跑 100 分,但 CrUX 里你的移动端 p75 LCP 是 4 秒——排名该扣还是扣。
CrUX 的覆盖有门槛:一个站点必须有足够的真实流量,Google 才会采集和公开它的数据。中小站点如果流量不够,在 PageSpeed Insights 里会显示"现场数据不可用"——这时候你必须自建 RUM,否则你根本不知道真实用户的体验。
五、怎么收集现场数据
三条路,从轻到重:
1. web-vitals 库(强烈推荐,自家 RUM 的基础)
Google 官方维护的 web-vitals 库,浏览器端几行代码就能拿到 LCP/INP/CLS/TTFB/FCP,然后你想上报到哪都行。
// src/app/web-vitals.ts
import { onCLS, onINP, onLCP, onTTFB, onFCP } from 'web-vitals';
function sendToAnalytics(metric: { name: string; value: number; id: string; rating: string }) {
// 用 sendBeacon,页面卸载时也能发出去
const body = JSON.stringify({
name: metric.name, // 'LCP' | 'INP' | 'CLS' | ...
value: metric.value, // 数值,比如 LCP 的毫秒数
rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
id: metric.id, // 本次会话的唯一 id,便于去重
page: location.pathname,
// 维度:设备 / 网络 / 来源,自己加
device: /Mobi|Android/i.test(navigator.userAgent) ? 'mobile' : 'desktop',
referrer: document.referrer,
});
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/vitals', body);
} else {
fetch('/api/vitals', { body, method: 'POST', keepalive: true });
}
}
export function reportWebVitals() {
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
onTTFB(sendToAnalytics);
onFCP(sendToAnalytics);
}
// src/app/layout.tsx(Client Component)
'use client';
import { useEffect } from 'react';
import { reportWebVitals } from '@/app/web-vitals';
export function WebVitalsBootstrap() {
useEffect(() => { reportWebVitals(); }, []);
return null;
}
注意 sendBeacon / fetch(..., keepalive: true) 的用法——INP 和 CLS 的最终值往往在用户离开页面时才确定,必须用能在 unload 期间发出去的 API,否则会丢数据。
2. 接到现成的后端
上报端点 /api/vitals 收到数据后,你可以:
- 转发到 Google Analytics 4(用
gtag('event', 'web_vitals', {...}))——零成本,适合个人站点; - 发到 Sentry / Datadog / New Relic——这些 APM 都有 RUM 模块,自带看板和告警;
- 写进自建数据表(比如 Postgres + 一个简单的聚合查询),完全自控。
3. CrUX(被动享受)
如果你的站流量够,Google 会自动采集,你可以直接在 PageSpeed Insights 或 Search Console 的"核心网页指标"里看结果。免运维,但只能看聚合后的域名级数据,无法下钻到具体页面或用户群(除非走 CrUX API/CrUX Dashboard,也仍是月度聚合)。
实战建议:web-vitals 库自建 RUM 必装,CrUX 当交叉验证。前者实时、可下钻;后者权威、可对比竞品。
六、分析现场数据:看 p75,不是平均
拿到一堆现场数据后,不要看平均值。性能数据是极度长尾分布(99 个用户 1 秒 + 1 个用户 30 秒,平均 1.29 秒,但那个 30 秒的用户早就走了),平均值会掩盖一切。
业界标准是看 p75(75 分位数):把所有样本从小到大排,第 75% 那个位置的值。意思是"75% 的用户体验至少这么好"。Google 的 CrUX、所有 Web Vitals 达标判定,用的都是 p75。
更进一步,一定要切片看:
- 按设备切片:"移动端 p75 LCP 是多少?桌面端呢?"——答案常常差 2-3 倍。
- 按网络切片:"4G 用户的 p75 INP?慢速 3G 的呢?"
- 按页面切片:"商品详情页 vs 首页,谁的 CLS 更糟?"
- 按路由/入口切片:"从搜索来的用户和从社媒来的,体验一样吗?"
一句实战黑话:"整体 p75 达标"是没意义的,"移动端 p75 LCP 达标"才是真达标。因为差的永远是移动端 + 慢网络那一拨用户,把他们单拎出来看,才知道问题在哪。
七、两种数据各自的正确用途
别把两者对立,它们是互补的:
| 场景 | 用什么 | 为什么 |
|---|---|---|
| 本地开发、改完代码立刻验证 | 实验室数据(Lighthouse) | 可复现、反馈快、不需要等用户访问 |
| CI 性能回归门禁 | 实验室数据 | 每次 PR 跑一遍,数字稳定才能定阈值 |
| 评估生产是否真的达标 | 现场数据(RUM / CrUX) | 反映真实体验,Google 排名也看这个 |
| 找具体页面/用户群的体验短板 | 现场数据 | 只有它能下钻到设备和网络维度 |
| 对比竞品、看行业排名 | CrUX | 公共数据集,可比性强 |
一句话:实验室数据管"开发循环",现场数据管"业务判定"。
八、别只优化 Lighthouse 分
这是个真实的坑。我见过团队为了把 Lighthouse 从 90 干到 100,做了一堆"模拟环境专用"的优化:精简字体、延迟加载一切、预连接一堆域名……结果 Lighthouse 确实 100 了,但现场数据几乎没动。因为这些优化针对的瓶颈在模拟节流下被放大了,而真实用户的瓶颈根本不在那里(真实用户可能卡在第三方 SDK、卡在主线程长任务、卡在低端机的 hydration)。
更糟的是,有些"冲分"操作反而伤害真实体验:比如过度延迟加载首屏图片,在快速网络 + 好设备上是省资源的好事,但在弱设备上却让 LCP 触发更晚。
所以最终铁律:
Lighthouse 是开发期的指南针,CrUX/RUM 才是生产期的裁判。优化以现场数据为目标,实验室数据当辅助。
当你下次又想把那个 Lighthouse 分往上怼两分时,先打开你的 RUM 看板,问问自己:"这次改动,会让移动端 p75 LCP 降下来吗?"如果答案是"不知道",那这个优化先缓缓。
小结
- 实验室数据(Lighthouse) 在模拟、干净、可复现的环境里测,适合开发调试和 CI 回归;现场数据(RUM/CrUX) 来自真实用户,反映真实体验,是生产质量的最终判定。
- 两者差距来自真实世界的变量:慢设备、差网络、插件、缓存、用户行为——实验室模拟不了,所以现场数据通常比实验室差 2-5 倍。
- Google 排名用的是 CrUX 现场数据,不是 Lighthouse 分。流量不够进不了 CrUX 的站点,必须自建 RUM。
- 收现场数据用
web-vitals库 +sendBeacon上报,看数据用 p75 + 按设备/网络/页面切片,"移动端 p75"才是真达标。 - 别只优化 Lighthouse 分——它可能让模拟环境变好而现场毫无变化。以现场数据为准,实验室数据当开发期辅助。
金句:实验室数据告诉你"你的页面能跑多快";现场数据告诉你"你的用户到底有多慢"。优化性能,最终要赢的是后者那场仗。
Related