Lighthouse 绿分不代表真实体验:CrUX 现场数据与实验室数据的口径差异解读
上周帮一个做汽配出口的独立站看性能(站主化名老周),Lighthouse 手机端跑出来 LCP 2.1 秒、总分 98,绿色满格。他把截图发给曼彻斯特的代理,代理回了一句:你们官网在我手机上要转五六秒才出图。老周第一反应是代理的手机不行,第二反应才是——这两套数据根本不是一个口径。这事在 SEO(Search Engine Optimization,搜索引擎优化)圈子里天天发生,因为 Google 排名用的 Core Web Vitals 信号来自 CrUX 现场数据,跟你本地跑一次 Lighthouse 没有直接关系。
先搞清楚:一个是模拟考试,一个是监控录像
Lighthouse 是实验室(lab)数据。它在你本地或 CI 里开一个无头浏览器,模拟一台中端安卓手机(Moto G Power 规格),CPU 减速 4 倍,网络限速到慢速 4G 档,把页面加载一遍,记录这一遍的表现。好处是可复现、可调试、每次环境一致,坏处是这个环境跟你真实用户的设备和网络没有必然联系。

CrUX(Chrome UX Report,Chrome 用户体验报告)是现场(field)数据。Chrome 会把一部分自愿参与统计的用户的加载与交互性能数据汇总上报,Google 按 28 天滚动窗口聚合成分布,取 75 分位(p75)作为评级依据。也就是说,CrUX 回答的是「过去 28 天,真实用户里 75% 的人体验如何」,Lighthouse 回答的是「在固定实验室环境里,这一次加载表现如何」。
排名信号看现场,调试定位看实验室,两件事别混着用。
把口径摆在一起看:
| 维度 | CrUX 现场数据 | Lighthouse 实验室数据 |
|---|---|---|
| 数据来源 | 真实 Chrome 用户上报(RUM 汇总) | 本地或 CI 单次模拟加载 |
| 采集窗口 | 28 天滚动更新 | 单次运行,无历史 |
| 设备分布 | 用户真实手机/电脑混合 | 固定模拟中端安卓 |
| 网络环境 | 真实 4G/WiFi/弱网混合 | 固定限速档 |
| 样本量 | 页面级需足够流量,否则降级 origin | 恒为 1 |
| 指标取值 | p75 分位 + 直方图分布 | 单次测量值 |
| 能否复现 | 不能逐次复现 | 每次可复现 |
CrUX 的采集机制:真实用户、28 天、p75
这段是理解口径差异的核心,拆开说三层。
其一,数据来源。CrUX 不需要你在页面里装任何探针,数据来自 Chrome 浏览器本身。用户首次使用 Chrome 时同意了使用情况统计,浏览器就会匿名上报页面加载事件(比如 LCP 元素何时渲染完成)和交互事件(比如某次点击到界面响应隔了多久)。所以你没主动接入 RUM 的站点也可能有 CrUX 数据,只要访客里用 Chrome 且同意统计的人足够多。
其二,28 天滚动窗口。CrUX 没有单日数据,任何查询返回的都是过去 28 天的聚合。今天发版,性能改动不会立刻反映进去,通常要等几个滚动周期才能在分位上看清趋势。老周当时改完图片就天天刷 CrUX,刷了三天没变化,以为白改了,其实是窗口还没滚过去。想看趋势得用 CrUX History API,它能把 25 周内的分位序列拉出来画折线。
其三,p75 分位。Google 取每个指标分布的 75 分位做评级,不看平均值。原因是平均值会被大量快体验掩盖慢体验:假设 80% 的用户 1.5 秒完成 LCP,20% 的用户要 9 秒,平均值 3 秒看着还行,但那 20% 可能正好是你在移动网络上浏览、付费意愿又不低的那批买家。p75 迫使你照顾体验较差的四分之一用户。分布本身以直方图返回,能直接看到「好 / 需要改进 / 差」三档各占多少比例,比一个孤零零的分数有用得多。
| 指标 | 全称 | 度量什么 | p75 良好阈值 | 现场超标的常见原因 |
|---|---|---|---|---|
| LCP | Largest Contentful Paint(最大内容绘制) | 主内容何时渲染完成 | ≤ 2.5 秒 | 首图过大未压缩、未预加载、慢 CDN |
| INP | Interaction to Next Paint(下次绘制交互延迟) | 全程交互响应延迟的最差水平 | ≤ 200 毫秒 | 长任务阻塞主线程、重型第三方脚本 |
| CLS | Cumulative Layout Shift(累积布局偏移) | 页面元素意外跳动的程度 | ≤ 0.1 | 图片无宽高、广告位晚注入、字体交换 |
INP 取代 FID 已经有一段时间了:FID 只测第一次输入的延迟,INP 统计页面整个生命周期里的所有交互,取接近最差的那次(约 98 分位)。做网站优化时别再盯着 FID 的旧资料调参数。
页面级和 origin 级别:流量不够会自动降级
CrUX 有两个粒度。origin 级别是整个域名(含全部子域名与路径)的聚合,只要站点有 Chrome 流量基本都查得到;page 级别是单个 URL 的聚合,前提是这个 URL 在 28 天窗口内攒够了样本——Google 没公开确切阈值,实践中一般得有稳定的日均访问量。用 API 查页面级时,样本不够会返回错误或空记录;PSI 网页版会悄悄改展示 origin 数据,很多人没注意页面顶部那一行小字,拿整站聚合当成了自己某个落地页的成绩,优化方向直接跑偏。
为什么实验室满分,现场还是「需要改进」
老周的站点就是典型,拆开看有四个原因。
设备与网络分布对不上。Lighthouse 模拟的是一台固定的中端手机加固定限速档,真实用户的设备从旗舰机到五年前的老机器都有,网络从写字楼光纤到地铁里的弱信号都有。CrUX 的 p75 反映的是真实分布,如果你的买家大量在移动网络下逛站,p75 被拉高太正常了。
指标口径本身不同。Lighthouse 一直拿不到真实交互数据,它用 TBT(Total Blocking Time,总阻塞时间)间接估计交互卡顿;CrUX 上报的是真实 INP,是用户实际点击、输入时测出来的。一个页面 TBT 很低,但某段代码在特定数据量下卡 400 毫秒,实验室测不到,用户测得到。
LCP 元素可能根本不是同一个。实验室每次都是首次访问,缓存是空的;真实用户里回访者不少,他们的缓存命中情况、看到的首屏素材都可能不同。首屏图如果做了 A/B 或按地区换素材,现场采集到的 LCP 元素和你本地看到的那张图可能对不上号。
第三方脚本的方差。客服插件、广告像素、带追踪参数的重定向,这些东西在高分位上的表现比单次模拟糟糕得多。Lighthouse 那一次跑的时候第三方可能恰好没拖后腿,p75 不会给你这种运气。
换个说法:Lighthouse 证明「这个构建没有明显硬伤」,CrUX 才证明「用户过得好不好」。两个都绿才真的稳。
动手拉数据:CrUX API 与 PageSpeed Insights API 的字段
两套 API 都是官方的。CrUX API 需要 Google Cloud 的 API Key;PageSpeed Insights API 不带 Key 也能调,但配额很低,建议同样带上 Key。
CrUX API 是 POST 接口,查一条聚合记录:
# 依赖:Python 3.10+、requests 2.x
# 环境:本地脚本即可,先在 Google Cloud Console 开通 Chrome UX Report API 并创建 API Key
import requests
# API Key 别硬编码进仓库,示例里为了直观才直接写
API_KEY = "你的-CRUX-API-KEY"
# 请求体:url 与 origin 二选一
# 先试页面级(url),样本不够再降级 origin 拿整站聚合
payload = {
"url": "https://example.com/products/bearing-6204/",
# formFactor 可选 PHONE / DESKTOP / TABLET / ALL
# 按设备拆开查,才能定位「手机慢还是电脑慢」
"formFactor": "PHONE",
# 显式列出要的指标,控制响应体积
"metrics": [
"largest_contentful_paint",
"interaction_to_next_paint",
"cumulative_layout_shift",
],
}
# 注意:CrUX API 只接受 POST,用 GET 会直接 404
resp = requests.post(
"https://chromeuxreport.googleapis.com/v1/records:queryRecord",
params={"key": API_KEY},
json=payload,
timeout=30,
)
data = resp.json()
# 正常响应结构:record.metrics 是指标字典,collectionPeriod 是窗口起止
# 页面级样本不足时返回 error,典型原因是 28 天内该 URL 的 Chrome 访问太少
if "error" in data:
print("页面级无数据:", data["error"].get("message"))
# 降级到 origin:拿到的是整站聚合,别当成这个落地页的成绩
payload.pop("url")
payload["origin"] = "https://example.com"
resp = requests.post(
"https://chromeuxreport.googleapis.com/v1/records:queryRecord",
params={"key": API_KEY},
json=payload,
timeout=30,
)
data = resp.json()
# record.metrics 里每个指标都带 percentiles.p75 和 histogram 三档占比
for name, metric in data["record"]["metrics"].items():
p75 = metric["percentiles"]["p75"]
# category 是该指标评级:good / needs-improvement / poor
print(name, "p75 =", p75, "评级 =", metric.get("category", "查直方图"))
PSI API 更省事,一次请求同时带回两套口径:loadingExperience 是现场数据(本质就是 CrUX),lighthouseResult 是实验室单次模拟:
// 依赖:Node 18+(内置 fetch),无第三方包
// 环境:本地脚本即可;PSI API 不带 Key 能调但配额低,建议带上 Key
// 保存为 .mjs 才能使用顶层 await
const PSI = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed";
// strategy=mobile 与 Lighthouse 默认的移动模拟口径对齐
// category=performance 表示只要性能类审计结果
const target =
`${PSI}?url=${encodeURIComponent("https://example.com/")}` +
"&strategy=mobile&category=performance";
const res = await fetch(target);
const json = await res.json();
// loadingExperience 是现场数据,overall_category 是现场整体评级
const field = json.loadingExperience;
console.log("现场评级:", field.overall_category);
// lighthouseResult 才是实验室单次模拟的结果,别和现场混着看
const labLcp =
json.lighthouseResult.audits["largest-contentful-paint"].displayValue;
console.log("实验室 LCP:", labLcp);
// 现场指标取 p75 分位,字段名和 CrUX API 一致
for (const [name, metric] of Object.entries(field.metrics)) {
// percentile 就是 p75 数值,category 是 good / needs-improvement / poor
console.log(name, metric.percentile, metric.category);
}
字段对照一下:两套接口的 metrics 里都是 largest_contentful_paint、interaction_to_next_paint、cumulative_layout_shift 三个键,每个键下有 percentiles.p75、histogram(三档占比)、category(评级)。现场显示「需要改进」,就是 p75 落在了中间档。origin 与 page 的选择也在这层体现:PSI 返回里有 loadingExperience 的 origin_fallback 字段,为 true 说明你看到的其实是整站数据。
用两套数据定位真实瓶颈的流程
把流程画出来:
flowchart TD
A[拉 CrUX 现场数据<br/>按 PHONE / DESKTOP 拆开] --> B{p75 是否达标}
B -- 达标 --> C{实验室是否也达标}
C -- 达标 --> D[收工,持续监控 28 天滚动变化]
C -- 不达标 --> E[按 Lighthouse 诊断项优化<br/>但别为分数砍功能]
B -- 不达标 --> F{实验室是否达标}
F -- 不达标 --> G[瓶颈明确<br/>按 Lighthouse 诊断项直接修]
F -- 达标 --> H[口径差异问题<br/>查真实设备/网络分布<br/>补自采 RUM、盯第三方脚本]
落到操作上,再配一条数据获取链路:
sequenceDiagram
participant S as 站长脚本
participant C as CrUX API
participant P as PageSpeed Insights API
S->>C: POST queryRecord(origin/url, formFactor)
C-->>S: LCP / INP / CLS 的 p75 与直方图(28 天滚动)
S->>P: GET runPagespeed(url, strategy=mobile)
P-->>S: loadingExperience(现场)+ lighthouseResult(实验室)
S->>S: 同指标对齐比较,按四象限定位瓶颈
四个象限的处置:现场差、实验室差,瓶颈明确,直接按诊断项修;现场差、实验室好,大概率是设备或网络分布问题,去补自采 RUM(用 web-vitals 库把真实用户数据回传自己的统计)看真实用户的 LCP 元素是什么、INP 卡在哪次交互;现场好、实验室差,多半是样本太瘦或者实验室环境敏感,优化照做但别牺牲功能;两个都好,进入监控状态。
老周的案例走完一遍:CrUX 显示 PHONE 的 LCP p75 是 4.8 秒、DESKTOP 是 2.2 秒,而 Lighthouse 手机模拟只有 2.1 秒。差距来自英国买家的移动网络,加上首屏一张 1.2MB 的横幅图没压缩也没预加载,实验室的模拟网络比真实 4G 仁慈。把图压到 300KB 加上 preload,等了三个滚动周期,PHONE 的 p75 降到 2.6 秒,PSI 现场评级从「需要改进」回到「良好」。做技术SEO 和独立站自然流量增长的时候,这类修复带来的排名收益往往比堆关键词实在。
收尾:两个误区和一句提醒
误区一,把单次 Lighthouse 分数当 KPI 汇报。分数会随环境抖动,评级看的是 28 天 p75,两者没有换算关系。误区二,只盯 CrUX。新页面、新站点没有现场数据(样本不够),上线前的把关还得靠实验室跑分,等流量起来现场数据才有意义。
趋势上,Google 把评级口径钉死在真实用户 p75 上,等于把性能优化的验收标准从「开发者环境」挪到了「买家环境」。对做外贸独立站的人来说,买家环境大概率是跨洋移动网络,这个差距只会在 CrUX 里现形。
至于 AI 搜索那一层,把 Core Web Vitals 做扎实之后,站点在传统 SEO 排名上直接受益,对生成式引擎优化(Generative Engine Optimization, GEO)需要的抓取与解析也更省事——不管是人还是模型,都喜欢响应快、不跳版的页面。你的站点有没有出现过实验室满分、现场需要改进的情况,评论区聊聊是怎么排查的。
参考与延伸
- Chrome UX Report(CrUX)方法论与口径说明:https://developer.chrome.com/docs/crux/methodology
- PageSpeed Insights API 官方入门文档:https://developers.google.com/speed/docs/insights/v5/get-started
- Lighthouse 官方文档(实验室环境说明):https://developer.chrome.com/docs/lighthouse/overview/
- Core Web Vitals 指标定义与阈值:https://web.dev/articles/vitals
关键词:Core Web Vitals、Lighthouse、CrUX、PageSpeed Insights、SEO、网站优化、技术SEO、独立站自然流量