衡量现场 Web Vitals 的最佳实践

如何使用您当前的分析工具衡量 Web Vitals。

能够衡量和报告页面的真实世界性能对于诊断和长期改进性能至关重要。如果没有现场数据,就无法确定您对网站所做的更改是否真正实现了预期的结果。

许多流行的 真实用户监控 (RUM) 分析提供商已经在他们的工具中支持 Core Web Vitals 指标(以及许多其他 Web Vitals)。如果您目前正在使用这些 RUM 分析工具之一,那么您的情况非常好,可以评估您网站上的页面在多大程度上满足 推荐的 Core Web Vitals 阈值,并防止未来出现衰退。

虽然我们建议使用支持 Core Web Vitals 指标的分析工具,但如果您当前使用的分析工具不支持这些指标,您不一定需要切换。几乎所有分析工具都提供定义和衡量自定义指标事件的方法,这意味着您很可能可以使用当前的分析提供商来衡量 Core Web Vitals 指标,并将它们添加到您现有的分析报告和仪表板中。

本指南讨论了使用第三方或内部分析工具衡量 Core Web Vitals 指标(或任何自定义指标)的最佳实践。它也可以作为分析供应商希望在其服务中添加 Core Web Vitals 支持的指南。

使用自定义指标或事件

如上所述,大多数分析工具都允许您衡量自定义数据。如果您的分析工具支持此功能,您应该能够使用此机制衡量每个 Core Web Vitals 指标。

在分析工具中衡量自定义指标或事件通常是一个三步过程

  1. 定义或注册您工具的管理界面中的自定义指标(如果需要)。(注意:并非所有分析提供商都要求提前定义自定义指标。)
  2. 在您的前端 JavaScript 代码中计算指标的值。
  3. 将指标值发送到您的分析后端,确保名称或 ID 与步骤 1 中定义的名称或 ID 匹配(同样,如果需要)

对于步骤 1 和 3,您可以参考您的分析工具的文档以获取说明。对于步骤 2,您可以使用 web-vitals JavaScript 库来计算每个 Core Web Vitals 指标的值。

以下代码示例展示了在代码中跟踪这些指标并将它们发送到分析服务是多么容易。

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

function sendToAnalytics({name, value, id}) {
  const body = JSON.stringify({name, value, id});
  // Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
  (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
      fetch('/analytics', {body, method: 'POST', keepalive: true});
}

onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);

避免平均值

通过计算平均值来总结性能指标的一系列值是很诱人的。平均值乍一看似乎很方便,因为它们是对大量数据的整洁总结,但您应该抵制依赖它们来解释页面性能的冲动。

平均值是有问题的,因为它们不代表任何单个用户的会话。分布范围两端的异常值可能会以误导性的方式扭曲平均值。

例如,一小部分用户可能位于极慢的网络或设备上,这些网络或设备的值接近最大范围,但用户会话不足以影响平均值,从而表明存在问题。

尽可能依赖百分位数而不是平均值。给定性能指标的分布中的百分位数更好地描述了您网站的完整用户体验范围。这使您可以专注于实际体验的子集,这将比单个值提供更多的见解。

确保您可以报告分布

一旦您计算出每个 Core Web Vitals 指标的值,并使用自定义指标或事件将它们发送到您的分析服务,下一步就是构建报告或仪表板,显示已收集的值。

为了确保您满足 推荐的 Core Web Vitals 阈值,您需要您的报告显示每个指标在第 75 个百分位数的值。

如果您的分析工具未将分位数报告作为内置功能提供,您可能仍然可以通过生成一个报告来手动获取此数据,该报告列出按升序排序的每个指标值。一旦生成此报告,在报告中所有值的完整排序列表中 75% 位置的结果将是该指标的第 75 个百分位数——无论您如何细分数据(按设备类型、连接类型、国家/地区等),情况都是如此。

如果您的分析工具默认情况下不为您提供指标级别的报告粒度,如果您的分析工具支持自定义维度,您很可能可以实现相同的结果。通过为您跟踪的每个单独的指标实例设置唯一的自定义维度值,如果您在报告配置中包含自定义维度,您应该能够生成按单个指标实例细分的报告。由于每个实例都将具有唯一的维度值,因此不会发生分组。

在正确的时间发送您的数据

某些性能指标可以在页面加载完成后计算,而其他指标(如 CLS)会考虑页面的整个生命周期——并且只有在页面开始卸载后才最终确定。

然而,这可能会有问题,因为 beforeunloadunload 事件都不可靠(尤其是在移动设备上),并且不建议使用它们 (因为它们会阻止页面符合 后退/前进缓存 的条件)。

对于跟踪页面整个生命周期的指标,最好在 visibilitychange 事件期间发送指标的当前值,只要页面的可见性状态更改为 hidden。这是因为——一旦页面的可见性状态更改为 hidden——就无法保证该页面上的任何脚本都能够再次运行。在移动操作系统上尤其如此,在移动操作系统上,浏览器应用程序本身可以在没有任何页面回调被触发的情况下关闭。

请注意,移动操作系统通常会在切换选项卡、切换应用程序或关闭浏览器应用程序本身时触发 visibilitychange 事件。它们还会在关闭选项卡或导航到新页面时触发 visibilitychange 事件。这使得 visibilitychange 事件比 unloadbeforeunload 事件可靠得多。

长期监控性能

一旦您更新了分析实现,以跟踪和报告 Core Web Vitals 指标,下一步是跟踪对您网站的更改如何随时间影响性能。

对您的更改进行版本控制

跟踪更改的一种幼稚(且最终不可靠)的方法是将更改部署到生产环境,然后假设部署日期之后收到的所有指标都对应于新站点,而部署日期之前收到的所有指标都对应于旧站点。但是,许多因素(包括 HTTP、服务工作线程或 CDN 层的缓存)都可能阻止此方法起作用。

更好的方法是为每个部署的更改创建一个唯一的版本,然后在您的分析工具中跟踪该版本。大多数分析工具都支持设置版本。如果您的工具不支持,您可以创建一个自定义维度,并将该维度设置为您部署的版本。

运行实验

您可以通过同时跟踪多个版本(或实验)来进一步推进版本控制。

如果您的分析工具允许您定义实验组,请使用该功能。否则,您可以使用自定义维度来确保您的每个指标值都可以与报告中的特定实验组关联。

通过在分析中进行实验,您可以将实验性更改推广到一部分用户,并将该更改的性能与对照组用户的性能进行比较。一旦您确信更改确实提高了性能,您就可以将其推广给所有用户。

确保测量不会影响性能

在衡量真实用户的性能时,绝对关键的是,您运行的任何性能测量代码都不会对页面的性能产生负面影响。如果确实如此,那么您尝试就您的性能如何影响您的业务得出的任何结论都将不可靠,因为您永远不会知道分析代码本身的存在是否具有最大的负面影响。

在您的生产站点上部署 RUM 分析代码时,始终遵循以下原则

延迟您的分析

分析代码应始终以异步、非阻塞的方式加载,并且通常应最后加载。如果您以阻塞方式加载分析代码,则可能会对 LCP 产生负面影响。

用于衡量 Core Web Vitals 指标的所有 API 都经过专门设计,以支持异步和延迟脚本加载(通过 buffered 标志),因此无需急于尽早加载脚本。

如果您要衡量的指标无法在页面加载时间线的后期计算,您应该将需要尽早运行的代码内联到文档的 <head> 中(因此它不是 渲染阻塞请求),并延迟其余代码。不要仅仅因为单个指标需要而尽早加载所有分析。

不要创建长任务

分析代码通常响应用户输入而运行,但如果您的分析代码正在进行大量 DOM 测量或使用其他处理器密集型 API,则分析代码本身可能会导致输入响应缓慢。此外,如果包含分析代码的 JavaScript 文件很大,则执行该文件可能会阻塞主线程并对页面的 Interaction to Next Paint (INP) 产生负面影响。

使用非阻塞 API

诸如 sendBeacon()requestIdleCallback() 之类的 API 专门设计用于以不阻塞用户关键任务的方式运行非关键任务。

这些 API 是在 RUM 分析库中使用的绝佳工具。

通常,所有分析信标都应使用 sendBeacon() API(如果可用)发送,并且所有被动分析测量代码都应在空闲期间运行。

不要跟踪超出您需要的范围

浏览器公开了大量性能数据,但仅仅因为数据可用并不一定意味着您应该记录它并将其发送到您的分析服务器。

例如,Resource Timing API 为页面上加载的每个资源提供详细的计时数据。但是,所有这些数据不太可能对于改进资源加载性能是必要或有用的。

简而言之,不要仅仅因为数据存在就跟踪数据,请确保在消耗资源跟踪数据之前将使用该数据。