为性能数据附加调试信息,通过分析数据识别并修复真实用户遇到的问题。
Google 提供两类性能测量和调试工具:
- 实验室工具:例如 Lighthouse,在模拟环境中加载页面,模拟慢网络、低端移动设备等条件。
- 现场工具:例如 Chrome 用户体验报告(CrUX),基于 Chrome 真实用户的聚合数据。PageSpeed Insights 和 Search Console 报告的现场数据也来自 CrUX。
现场工具的数据更能代表真实用户体验,实验室工具则通常更擅长帮助定位和修复问题。CrUX 分数反映实际性能,但只知道分数,未必能知道如何改进。
Lighthouse 会识别问题并给出具体建议,但它只针对页面加载期间发现的性能问题;滚动、点击按钮等用户交互才触发的问题,不在这类检测范围内。
因此,如何从真实用户访问中收集 Core Web Vitals 或其他性能指标的调试信息?本文介绍可用于各项核心指标的 API,以及如何把额外调试信息接入现有分析工具。
用于归因和调试的 API
累积布局偏移(CLS)
在所有核心指标中,CLS 可能最需要现场调试信息。它在页面整个生命周期内测量;用户滚动多远、点击什么,都会影响是否产生布局偏移,以及哪些元素移动。
看下面的 PageSpeed Insights 报告:

Lighthouse 与 CrUX 报告的 CLS 相差很大是合理的,因为页面可能包含大量 Lighthouse 测试时未触发的交互内容。但即使知道交互影响现场数据,仍需知道哪些元素发生移动,使第75百分位的分数达到0.28。LayoutShiftAttribution 可以提供这些信息。
获取布局偏移归因
Layout Instability API 发出的每个 layout-shift 条目都会暴露 LayoutShiftAttribution。两个接口的详细说明见调试布局偏移。关键是开发者能够观察页面中的每次偏移,以及移动的元素。
以下示例记录每次偏移及移动元素:
new PerformanceObserver((list) => {
for (const {value, startTime, sources} of list.getEntries()) {
// Log the shift amount and other entry info.
console.log('Layout shift:', {value, startTime});
if (sources) {
for (const {node, curRect, prevRect} of sources) {
// Log the elements that shifted.
console.log(' Shift source:', node, {curRect, prevRect});
}
}
}
}).observe({type: 'layout-shift', buffered: true});
把所有偏移都测量并发送到分析工具通常不现实;但持续观察它们,可以追踪最严重的偏移,只上报这些信息。
目标并非修复每个用户遇到的每一次偏移,而是识别影响人数最多、对页面第75百分位 CLS 贡献最大的偏移。也不必每次偏移都计算最大来源元素;准备上报 CLS 时再计算即可。
以下函数接收参与 CLS 计算的 layout-shift 条目,返回最大偏移中的最大来源元素:
function getCLSDebugTarget(entries) {
const largestEntry = entries.reduce((a, b) => {
return a && a.value > b.value ? a : b;
});
if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
const largestSource = largestEntry.sources.reduce((a, b) => {
return a.node && a.previousRect.width * a.previousRect.height >
b.previousRect.width * b.previousRect.height ? a : b;
});
if (largestSource) {
return largestSource.node;
}
}
}
识别后就可以将元素上报分析工具。不同用户的主要贡献元素可能不同;汇总所有用户后,可以得到影响人数最多的移动元素列表。
修复这些元素的根因后,分析代码就会把更小的偏移报告为当前“最严重”偏移。最终,所有上报偏移都可以足够小,使页面处于0.1 的良好阈值以内。
除最大来源元素,还可收集最大偏移发生时间,以及当时的 URL 路径;后者特别适用于会动态更新 URL 的单页应用。
最大内容绘制(LCP)
现场调试 LCP 最重要的信息,是这次页面加载中哪个元素最大,即 LCP 候选元素。即使完全相同的页面,不同用户的候选元素也经常不同:
因此不能想当然地认定某个或某组元素是常见 LCP 候选,必须基于真实用户行为测量。详情见实验室数据与现场数据为何不同。
识别 LCP 候选元素
可使用测量 LCP 时间值的同一个 Largest Contentful Paint API。观察 largest-contentful-paint 条目时,最后一个条目的 element 属性即当前候选:
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP element:', lastEntry.element);
}).observe({type: 'largest-contentful-paint', buffered: true});
确定候选元素后,把它与指标值一起上报。与 CLS 类似,这能帮助识别最值得优先优化的元素。还可以测量 LCP 各子阶段耗时,判断网站需要哪些具体优化。
交互到下一次绘制(INP)
更多现场调试方法见在现场找出慢交互。
INP 最重要的现场信息包括:交互的元素、交互类型,以及发生时间。慢交互的重要原因是主线程阻塞,尤其常见于 JavaScript 加载期间。知道慢交互是否主要发生在加载阶段,有助于选择修复方式。
INP 考虑完整交互延迟,包括运行事件监听器的时间,以及所有监听器执行完后绘制下一帧的时间。因此,识别常导致慢交互的目标元素与交互类型非常有用。
下面记录 INP 条目的目标、类型与时间:
function logINPDebugInfo(inpEntry) {
console.log('INP target element:', inpEntry.target);
console.log('INP interaction type:', inpEntry.name);
console.log('INP time:', inpEntry.startTime);
}
此例没有展示如何从 event 条目确定哪一条对应 INP,因为相关逻辑更复杂。下一节介绍用 web-vitals 库取得这些信息。
配合 web-vitals JavaScript 库使用
前面给出了收集额外调试信息并随指标上报的通用建议。自版本3起,web-vitals 提供 attribution 构建,暴露上述信息与其他归因信号。
以下通过额外的事件参数或自定义维度传送调试字符串,帮助找到性能问题根因:
import {onCLS, onINP, onLCP} from 'web-vitals/attribution';
function sendToGoogleAnalytics({name, value, id, attribution}) {
const eventParams = {
metric_value: value,
metric_id: id,
}
switch (name) {
case 'CLS':
eventParams.debug_target = attribution.largestShiftTarget;
break;
case 'LCP':
eventParams.debug_target = attribution.element;
break;
case 'INP':
eventParams.debug_target = attribution.interactionTarget;
break;
}
// Assumes the global `gtag()` function exists, see:
// https://developers.google.com/analytics/devguides/collection/ga4
gtag('event', name, eventParams);
}
onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);
示例面向 Google Analytics,但思路也适用于其他分析工具。它只上报一个调试信号,实际可以为每个指标收集多个信号。
例如调试 INP 时,可以收集交互元素、类型、时间、loadState、各交互阶段耗时,以及长动画帧数据等。attribution 构建暴露了更多信息:
import {onCLS, onINP, onLCP} from 'web-vitals/attribution';
function sendToGoogleAnalytics({name, value, id, attribution}) {
const eventParams = {
metric_value: value,
metric_id: id,
}
switch (name) {
case 'INP':
eventParams.debug_target = attribution.interactionTarget;
eventParams.debug_type = attribution.interactionType;
eventParams.debug_time = attribution.interactionTime;
eventParams.debug_load_state = attribution.loadState;
eventParams.debug_interaction_delay = Math.round(attribution.inputDelay);
eventParams.debug_processing_duration = Math.round(attribution.processingDuration);
eventParams.debug_presentation_delay = Math.round(attribution.presentationDelay);
break;
// Additional metric logic...
}
// Assumes the global `gtag()` function exists, see:
// https://developers.google.com/analytics/devguides/collection/ga4
gtag('event', name, eventParams);
}
onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);
全部调试信号见 web-vitals 归因文档。
报告与可视化数据
开始随指标收集调试信息后,下一步是跨用户聚合,寻找模式与趋势。不必处理每一个问题,尤其初期应优先处理影响用户最多的问题,这些通常也对 Core Web Vitals 分数造成最大负面影响。
GA4 用户可参阅通过 BigQuery 查询与可视化数据。
总结
现有性能 API 与 web-vitals 库,可以提供基于真实访问诊断性能所需的调试信息。虽然本指南聚焦 Core Web Vitals,这些概念同样适用于任何能用 JavaScript 测量的性能指标。
如果刚开始测量性能且已使用 Google Analytics,可以从 Web Vitals Report 工具入手,它已经支持核心指标的调试信息上报。
分析工具供应商可以使用这些技术改善产品,向用户提供更多调试信息,但无需局限于本文思路。本文旨在普遍适用于分析工具;具体工具能够且应该捕获、报告更丰富的细节。
如果 API 缺失的功能或信息妨碍指标调试,可以向 web-vitals-feedback@googlegroups.com 反馈。











暂无评论内容