在现场调试性能

了解如何使用调试信息归因性能数据,以便通过分析找出并解决真实用户问题

Google 提供了两类工具来衡量和调试性能:

  • 实验室工具:Lighthouse 等工具,可在模拟环境中加载您的网页,模拟各种条件(例如网络速度缓慢和使用低端移动设备)。
  • 现场工具Chrome 用户体验报告 (CrUX) 等工具,该报告基于 Chrome 中的汇总真实用户数据。(请注意,PageSpeed InsightsSearch Console 等工具报告的现场数据来自 CrUX 数据。)

虽然现场工具提供的数据更准确(这些数据实际上代表了真实用户的体验),但实验室工具通常更擅长帮助您发现和解决问题。

CrUX 数据更能代表网页的真实性能,但了解 CrUX 得分不太可能帮助您确定如何提升性能。

另一方面,Lighthouse 会找出问题并提出具体改进建议。不过,Lighthouse 只会针对在网页加载时发现的性能问题提供建议。它不会检测仅在用户互动(例如滚动网页或点击网页上的按钮)后才会出现的问题。

这引出了一个重要问题:如何从实际环境中的真实用户捕获 Core Web Vitals 或其他性能指标的调试信息?

本文将详细介绍您可以使用哪些 API 为每个当前 Core Web Vitals 指标收集其他调试信息,并为您提供有关如何在现有分析工具中捕获此类数据的提示。

用于归因和调试的 API

Cumulative Layout Shift (CLS)

在所有 Core Web Vitals 指标中,CLS 可能是收集现场调试信息最重要的指标。CLS 是在网页的整个生命周期内衡量的,因此用户与网页互动的方式(滚动距离、点击内容等)可能会显著影响是否存在布局偏移以及哪些元素发生了偏移。

请参考 PageSpeed Insights 中的以下报告:

CLS 值不同的 PageSpeed Insights 报告
PageSpeed Insights 会同时显示实测数据和实验室数据(如果有),这两者可能有所不同

实验室 (Lighthouse) 报告的 CLS 值与现场 (CrUX 数据) 报告的 CLS 值截然不同。如果您考虑到该网页可能包含大量在 Lighthouse 中测试时未使用的互动内容,就会明白这是正常现象。

不过,即使您了解用户互动会影响字段数据,也仍需要了解网页上的哪些元素发生了变化,导致得分在第 75 个百分位数处于 0.28。LayoutShiftAttribution 接口可实现这一点。

获取布局偏移归因

LayoutShiftAttribution 接口会在 Layout Instability API 发出的每个 layout-shift 条目中公开。

如需详细了解这两个接口,请参阅调试布局偏移。在本文中,您需要了解的主要内容是,作为开发者,您可以观察页面上发生的每一次布局偏移,以及发生偏移的元素。

下面是一些示例代码,这些代码会记录每次布局偏移以及发生偏移的元素:

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;
    }
  }
}

确定导致最大偏移的最大元素后,您可以将其报告给分析工具。

给定网页中对 CLS 贡献最多的元素可能因用户而异,但如果您汇总所有用户的这些元素,则可以生成一个列表,其中包含影响最多用户的易变元素。

确定并修正这些元素偏移的根本原因后,您的 Google Analytics 代码将开始将较小的偏移报告为网页的“最严重”偏移。最终,所有报告的偏移量都会足够小,您的网页会远远低于“良好”阈值 0.1

除了最主要的转移来源元素之外,可能还有一些其他元数据值得捕获,例如:

  • 最大偏移的时间
  • 出现最大转移时所用的网址路径(适用于动态更新网址的网站,例如单页面应用)。

Largest Contentful Paint (LCP)

如需在现场调试 LCP,您需要了解的主要信息是哪个特定元素是相应网页加载的最大元素(LCP 候选元素)。

请注意,即使是完全相同的网页,不同用户的 LCP 候选元素也完全可能不同(事实上,这种情况很常见)。

以下是可能导致此问题的原因:

  • 用户设备的屏幕分辨率不同,因此网页布局也不同,因此视口中显示的元素也不同。
  • 用户并不总是会将网页滚动到顶部。链接通常包含片段标识符,甚至包含文本片段,这意味着您的网页可能会在网页上的任何滚动位置加载并显示。
  • 内容可能会针对当前用户进行个性化,因此 LCP 候选元素可能会因用户而异。

这意味着,您无法假定哪个元素或元素组将是特定网页最常见的 LCP 候选元素。您必须根据真实用户行为来衡量它。

确定 LCP 候选元素

如需在 JavaScript 中确定 LCP 候选元素,您可以使用 Largest Contentful Paint API,即用于确定 LCP 时间值的 API。

观察 largest-contentful-paint 条目时,您可以通过查看上一个条目的 element 属性来确定当前的 LCP 候选元素:

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});

确定 LCP 候选元素后,您可以将其与指标值一起发送到分析工具。与 CLS 一样,这有助于您确定哪些元素最需要优先优化。

除了 LCP 候选元素之外,衡量 LCP 子部分时间也可能很有用,这有助于确定与您的网站相关的具体优化步骤。

Interaction to Next Paint (INP)

在 INP 字段中要捕获的最重要的信息是:

  1. 用户与哪个元素互动
  2. 互动类型
  3. 互动发生的时间

互动速度缓慢的主要原因是主线程被阻塞,在 JavaScript 加载期间,这种情况很常见。了解大多数互动速度缓慢的问题是否发生在页面加载期间,有助于确定需要执行哪些操作来解决问题。

INP 指标会考虑互动的完整延迟时间,包括运行所有已注册的事件监听器所需的时间,以及在所有事件监听器运行完毕后绘制下一帧所需的时间。这意味着,对于 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 JavaScript 库获取此类信息。

与 web-vitals JavaScript 库搭配使用

前面几部分提供了一些常规建议和代码示例,可帮助您捕获要包含在发送到分析工具的数据中的调试信息。

从版本 3 开始,web-vitals JavaScript 库包含一个归因 build,该 build 会显示所有这些信息以及一些额外信号

以下代码示例展示了如何设置包含调试字符串的其他事件参数(或自定义维度),该字符串有助于找出性能问题的根本原因。

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、互动阶段等(例如长动画帧数据)。

web-vitals 归因 build 会公开其他归因信息,如以下 INP 示例所示:

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 报告工具,因为该工具已支持报告 Core Web Vitals 指标的调试信息。

如果您是分析工具供应商,并希望改进自己的产品并向用户提供更多调试信息,不妨考虑采用本文中介绍的一些方法,但不要局限于本文中介绍的想法。本文旨在普遍适用于所有分析工具;不过,个别分析工具可能可以(也应)捕获和报告更多调试信息。

最后,如果您认为自己无法因 API 本身缺少功能或信息而调试这些指标,请发送电子邮件至 web-vitals-feedback@googlegroups.com 提供反馈。