衡量 Service Worker 对实际性能的影响

Service Worker 的最大优势之一(至少从性能的角度来看)是它们能够主动控制资源的缓存。对于回访者来说,可以缓存所有必要资源的网络应用会大大加快加载速度。但是,在真实用户看来,这些收益到底是什么样子?你们如何衡量这方面的表现呢?

Google I/O Web 应用(简称 IOWA)是一款 Progressive Web 应用,它利用了 Service Worker 提供的大多数新功能,为用户提供了类似应用的丰富体验。该公司还使用 Google Analytics(分析)从其庞大而多样化的用户受众群体中捕获关键的性能数据和使用模式。

本案例研究探讨 IOWA 如何使用 Google Analytics(分析)回答关键的性能问题,并报告 Service Worker 的实际影响。

从问题开始

每当您在网站或应用中实施 Google Analytics(分析)时,都必须先从您将收集的数据中找出自己想要解答的问题,这一点很重要。

虽然我们想要回答几个问题,但在本案例研究中,我们主要关注两个更有趣的问题。

1. Service Worker 缓存的性能是否比所有浏览器中提供的现有 HTTP 缓存机制都更高?

由于浏览器可以缓存请求,并在重复访问时立即处理请求,所以我们已经预计回访者的网页加载速度会比新访问者更快。

Service Worker 提供替代缓存功能,让开发者能够精细控制缓存的具体内容和方式。在 IOWA,我们优化了 Service Worker 的实现,每一个资源都得到缓存,这样回访者就可以完全离线使用应用程序。

但是,这样做会比浏览器默认所做的工作更好吗?如果有,改善了多少呢?1

2. Service Worker 对网站加载体验有何影响?

换言之,网站的加载速度有多快,而不考虑传统网页加载指标的实际加载时间?

回答有关体验感受的问题显然并非易事,而且没有哪个指标能完美体现这种主观情绪。尽管如此,确实有一些指标优于其他指标,因此选择合适的指标很重要。

选择合适的指标

默认情况下,Google Analytics(分析)会通过 Navigation Timing API 跟踪 1% 的网站访问者的网页加载时间,并通过平均网页加载时间等指标提供这些数据。

平均网页加载时间可以很好地满足我们的第一个问题,但对于第二个问题而言,这个指标不是特别好。首先,load 事件不一定对应于用户可以实际与应用互动的时刻。此外,两个加载时间完全相同的应用可能给人的感觉很不一样。例如,显示启动画面或加载指示器的网站看起来比仅显示空白页面的网站要快得多。

在爱荷华州,我们展示了一个启动画面倒计时动画,在我看来,该动画非常成功,可以非常有效地吸引用户,而应用的其余部分则在后台加载。因此,作为衡量用户感知的加载性能的方式,跟踪启动画面显示的时间会更有意义。我们选择了首次渲染时间指标来获取此值。

在确定了要回答的问题并确定有助于回答这些问题的指标后,我们就可以实施 Google Analytics(分析)并开始衡量效果了。

Analytics 实现

如果您以前使用过 Google Analytics(分析),那么应该比较熟悉我们推荐的 JavaScript 跟踪代码段。该架构如下所示:

<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics.js"></script>

上面代码中的第一行会初始化一个全局 ga() 函数(如果该函数尚不存在),而最后一行会异步下载 analytics.js 库。

中间部分包含以下两行:

ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');

这两个命令可跟踪访问您网站的用户访问了哪些网页,但不会收集更多数据。如果您希望跟踪其他用户互动,则必须自行跟踪。

对于 IOWA,我们希望跟踪另外两个方面:

  • 从网页首次开始加载到像素在屏幕上显示所用的时间。
  • Service Worker 是否正在控制页面。有了这些信息,我们可以对报告进行细分,以比较使用和不使用 Service Worker 的结果。

捕获首次绘制所用的时间

有些浏览器会记录第一个像素绘制到屏幕上的精确时间,并将这些时间提供给开发者。与通过 Navigation Timing API 公开的 navigationStart 值相比,该值可以非常准确地计算从用户最初请求网页到首次看到某些内容之间经过了多少时间。

如前所述,首次渲染时间是一个重要的衡量指标,因为它是用户体验到网站加载速度的第一个时间点。这是用户获得的第一印象,良好的第一印象会对用户体验的其余部分产生积极影响。2

为了在公开该值的浏览器中获取第一个绘制值,我们创建了 getTimeToFirstPaintIfSupported 实用函数:

function getTimeToFirstPaintIfSupported() {
  // Ignores browsers that don't support the Performance Timing API.
  if (window.performance && window.performance.timing) {
    var navTiming = window.performance.timing;
    var navStart = navTiming.navigationStart;
    var fpTime;

    // If chrome, get first paint time from `chrome.loadTimes`.
    if (window.chrome && window.chrome.loadTimes) {
      fpTime = window.chrome.loadTimes().firstPaintTime * 1000;
    }
    // If IE/Edge, use the prefixed `msFirstPaint` property.
    // See http://msdn.microsoft.com/ff974719
    else if (navTiming.msFirstPaint) {
      fpTime = navTiming.msFirstPaint;
    }

    if (fpTime && navStart) {
      return fpTime - navStart;
    }
  }
}

这样一来,我们现在可以编写另一个函数来发送非互动事件,并将首次绘制的时间作为其值:3

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    ga('send', 'event', {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    });
  }
}

在编写完这两个函数后,我们的跟踪代码将如下所示:

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sends a pageview for the initial pageload.
ga('send', 'pageview');

// Sends an event with the time to first paint data.
sendTimeToFirstPaint();

请注意,根据上述代码的运行时间,屏幕上不一定已绘制像素。为确保始终在首次绘制发生后运行此代码,我们将对 sendTimeToFirstPaint() 的调用推迟到 load 事件之后。事实上,我们决定将所有分析数据推迟到网页加载后发送,以确保这些请求不会与其他资源的加载竞争。

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

上述代码会向 Google Analytics(分析)报告 firstpaint 次,但这只是一半。我们仍然需要跟踪 Service Worker 的状态;否则,我们就无法比较由 Service Worker 控制的页面和非受控页面的首次绘制时间。

确定 Service Worker 的状态

为了确定 Service Worker 的当前状态,我们创建了一个实用函数,它会返回以下三个值之一:

  • controlled:由 Service Worker 控制页面。对于 IOWA,这也意味着所有资源都已缓存,网页可以离线工作。
  • supported:浏览器支持 Service Worker,但 Service Worker 尚未控制页面。这是初访者的预期状态。
  • unsupported:用户的浏览器不支持 Service Worker。
function getServiceWorkerStatus() {
  if ('serviceWorker' in navigator) {
    return navigator.serviceWorker.controller ? 'controlled' : 'supported';
  } else {
    return 'unsupported';
  }
}

此函数为我们获取 Service Worker 状态;下一步是将此状态与我们发送到 Google Analytics(分析)的数据相关联。

使用自定义维度跟踪自定义数据

默认情况下,Google Analytics(分析)提供了多种方法,可根据用户、会话或互动的特征对总流量进行细分。这些属性称为维度。Web 开发者会关注的常见维度包括浏览器操作系统设备类别

Service Worker 的状态不是 Google Analytics(分析)默认提供的维度;不过,Google Analytics(分析)允许您创建自己的自定义维度,并视需要定义这些维度。

对于爱荷华州,我们创建了名为“服务人员状态”的自定义维度,并将其范围设为“命中”(即每次互动)。4您在 Google Analytics(分析)中创建的每个自定义维度在该媒体资源中都有唯一的索引,在您的跟踪代码中,您可以通过索引来引用该维度。例如,如果我们刚刚创建的维度的索引为 1,我们可以按如下方式更新逻辑,以发送 firstpaint 事件以包含 Service Worker 的状态:

ga('send', 'event', {
  eventCategory: 'Performance',
  eventAction: 'firstpaint',
  // Rounds to the nearest millisecond since
  // event values in Google Analytics must be integers.
  eventValue: Math.round(timeToFirstPaint)
  // Sends this as a non-interaction event,
  // so it doesn't affect bounce rate.
  nonInteraction: true,

  // Sets the current service worker status as the value of
  // `dimension1` for this event.
  dimension1: getServiceWorkerStatus()
});

这是可行的,但它仅会将 Service Worker 的状态与此特定事件相关联。由于 Service Worker 状态对于任何互动可能很有用,因此最好在发送到 Google Analytics(分析)的所有数据中包含它。

为了在所有命中(如所有网页浏览、事件等)中纳入此信息,我们在向 Google Analytics(分析)发送任何数据之前,先在 tracker 对象本身上设置自定义维度值。

ga('set', 'dimension1', getServiceWorkerStatus());

设置完成后,此值将与当前网页加载的所有后续命中一起发送。如果用户稍后再次加载该网页,getServiceWorkerStatus() 函数很可能会返回新值,该值将在跟踪器对象上设置。

有关代码的清晰度和可读性的简要说明:由于查看此代码的其他人可能不知道 dimension1 所指的含义,因此最好创建一个变量,将有意义的维度名称映射到 analytics.js 将使用的值。

// Creates a map between custom dimension names and their index.
// This is particularly useful if you define lots of custom dimensions.
var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1'
};

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sets the service worker status on the tracker,
// so its value is included in all future hits.
ga('set', customDimensions.SERVICE_WORKER_STATUS, getServiceWorkerStatus());

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

正如前面提到的,每次命中时都发送 Service Worker 状态维度,可让我们在报告任何指标时使用该维度。

您可以看到,IOWA 有几乎 85% 的网页浏览来自支持 Service Worker 的浏览器。

调查结果:解答我们的疑问

我们开始收集数据来解答问题后,就可以生成关于这些数据的报告以查看结果。(注意:此处显示的所有 Google Analytics(分析)数据代表 2016 年 5 月 16 日至 22 日 IOWA 网站的实际网络流量。

我们遇到的第一个问题是:Service Worker 缓存的性能是否比所有浏览器中提供的现有 HTTP 缓存机制更高?

为解答这个问题,我们创建了自定义报告,该报告会跨多个维度查看“平均网页加载时间”指标。该指标非常适合回答这个问题,因为只有在所有初始资源下载完毕后,load 事件才会触发。因此,它可以直接反映网站的所有关键资源的总加载时间。5

我们选择的维度为:

  • 我们的自定义 Service Worker 状态维度。
  • 用户类型:指明这是用户首次访问网站还是回访用户。(注意:新访问者不会缓存任何资源;回访者可能会如此。)
  • 设备类别:可用于比较移动设备和桌面设备上的结果。

为控制非 Service Worker 相关因素影响加载时间结果的可能性,我们将查询限制为仅包含支持 Service Worker 的浏览器。

如您所见,由 Service Worker 控制的应用对应用的访问比未受控制的访问要快得多,即使是那些可能已被缓存大部分资源的回访用户的访问也是如此。有趣的是,我们发现,平均而言,使用 Service Worker 的移动设备访问者的加载速度比桌面设备上的新访问者更快。

“...由 Service Worker 控制的应用对我们应用的访问比未控制的访问加载快得多...”

您可以在以下两个表中查看更多详细信息:

平均网页加载时间(桌面设备)
Service Worker 状态 用户类型 平均网页加载时间(毫秒) 样本规模
操控了 回访者 2568 30860
支持 回访者 3612 1289
支持 新访者 4664 21991
平均网页加载时间(移动设备)
Service Worker 状态 用户类型 平均网页加载时间(毫秒) 样本规模
操控了 回访者 3760 8162
支持 回访者 4843 676
支持 新访者 6158 5779

您可能想知道,如果回访者的浏览器支持 Service Worker 是如何处于非受控状态的。导致这种情况的原因可能有以下几种:

  • 用户在 Service Worker 还没完成初始化之前,在初次访问时就离开了页面。
  • 用户通过开发者工具卸载了 Service Worker。

这两种情况比较少见。通过查看第四列中的“Page Load Sample”值,我们就能看到这一点。请注意,中间行的样本比另外两行小得多。

我们的第二个问题是:Service Worker 对网站加载体验有何影响?

为了回答这个问题,我们为“平均事件价值”指标创建了另一个自定义报告,并对结果进行过滤,使其仅包含“firstpaint”事件。我们使用了维度“设备类别”和自定义“Service Worker 状态”维度。

与我的预期相反,与对整体网页加载时间相比,移动设备上的 Service Worker 对首次绘制时间的影响要小得多。

"...与对整体页面加载相比,移动设备上的 Service Worker 对首次绘制时间的影响要小得多。”

为了探究背后的原因,我们必须更深入地了解相关数据。平均值既适用于笼统概览,又适合进行粗略分析,但若要真正了解这些数字在各用户群体中的细分情况,我们需要查看 firstpaint 次的分布情况。

获取 Google Analytics(分析)中指标的分布情况

为了获得 firstpaint 次的分布情况,我们需要获取每个事件的具体结果。遗憾的是,Google Analytics(分析)无法帮助您轻松做到这一点。

Google Analytics(分析)允许我们按所需维度对报告进行细分,但我们无法使用按指标细分报告。这并不是说不可能,只是表示我们必须对实现方式进行更多自定义,才能达到理想的效果。

由于报告结果只能按维度细分,因此我们必须将指标值(在本例中为 firstpaint 次)设置为该事件的自定义维度。为此,我们创建了另一个名为“指标值”的自定义维度,并更新了 firstpaint 跟踪逻辑,如下所示:

var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1',
  <strong>METRIC_VALUE: 'dimension2'</strong>
};

// ...

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    var fields = {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    }

    <strong>// Sets the event value as a dimension to allow for breaking down the
    // results by individual metric values at reporting time.
    fields[customDimensions.METRIC_VALUE] = String(fields.eventValue);</strong>

    ga('send', 'event', fields);
  }
}

Google Analytics(分析)网页界面目前无法直观呈现任意指标值的分布情况,但借助 Google Analytics Core Reporting APIGoogle 图表库,我们可以查询原始结果,然后自行构建直方图。

例如,以下 API 请求配置用于通过非受控 Service Worker 在桌面设备上获取 firstpaint 值的分布情况。

{
  dateRanges: [{startDate: '2016-05-16', endDate: '2016-05-22'}],
  metrics: [{expression: 'ga:totalEvents'}],
  dimensions: [{name: 'ga:dimension2'}],
  dimensionFilterClauses: [
    {
      operator: 'AND',
      filters: [
        {
          dimensionName: 'ga:eventAction',
          operator: 'EXACT',
          expressions: ['firstpaint']
        },
        {
          dimensionName: 'ga:dimension1',
          operator: 'EXACT',
          expressions: ['supported']
        },
        {
          dimensionName: 'ga:deviceCategory',
          operator: 'EXACT',
          expressions: ['desktop']
        }
      ],
    }
  ],
  orderBys: [
    {
      fieldName: 'ga:dimension2',
      orderType: 'DIMENSION_AS_INTEGER'
    }
  ]
}

此 API 请求返回如下所示的值数组(注意:这些只是前五个结果)。结果按从小到大的顺序排序,因此这些行表示用时最快。

API 响应结果(前五行)
ga:dimension2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

以下是这些结果的简明含义:

  • 有 3 个事件的 firstpaint 值为 4 毫秒
  • 有 2 个事件的 firstpaint 值为 5 毫秒
  • 有 10 个事件的 firstpaint 值为 6 毫秒
  • 有 8 个事件的 firstpaint 值为 7 毫秒
  • 有 10 个事件 firstpaint value 为 8 毫秒
  • 其他相关人员

根据这些结果,我们可以推断每个事件的 firstpaint 值,并创建分布直方图。我们对运行的每个查询都执行了此操作。

以下是使用未受控制(但受支持)的 Service Worker 在桌面设备上的分布情况:

桌面设备上的首次渲染时间分布(支持)

上述分布的 firstpaint 时间中位数为 912 毫秒

该曲线的形状是加载时间分布的典型形状。下面的直方图显示了 Service Worker 控制着网页的访问的首次绘制事件的分布情况。

桌面设备上的首次绘制分布时间(受控)

请注意,在 Service Worker 控制页面时,许多访问者会经历近乎即时的首次绘制,中位数为 583 毫秒

“...当 Service Worker 控制页面时,许多访问者几乎立即遇到首次绘制...”

为了更好地了解这两种分布模式之间的比较情况,下一张图表显示了这两种分布的合并视图。显示非受控 Service Worker 访问次数的直方图叠加显示在显示受控访问的直方图的上层,两者都叠加在显示两者组合的直方图的上层。

桌面设备上的首次绘制时间分布

关于这些结果,我发现有趣的一点是,在初始峰值之后,具有受控 Service Worker 的分布仍然具有钟形曲线。我预计最初会出现一个较大的峰值,然后逐渐下降,并不希望曲线会出现第二个峰值。

在调查导致此问题的原因时,我了解到,即使 Service Worker 可以控制页面,其线程也可能处于非活跃状态。浏览器这样做是为了节省资源 - 显然,您并不需要访问过每个网站的每个 Service Worker 都立即处于活动状态并随时做好准备。这就解释了分布的尾部。对于某些用户,Service Worker 线程启动时出现延迟。

但从分布可以看出,即使具有这种初始延迟,使用 Service Worker 的浏览器传递内容的速度也快于通过网络的浏览器。

以下是在移动设备上的显示效果:

移动设备上的首次绘制时间分布

虽然在近乎即时的首次渲染时间中仍有很大的增长,但尾部却很大一点,而且更长一些。这可能是因为在移动设备上,启动空闲的 Service Worker 线程需要比在桌面设备上启动的时间长。这也解释了平均 firstpaint 时间之间的差异没有达到预期(如上所述)的原因。

"...在移动设备上,启动空闲 Service Worker 线程需要比在桌面设备上启动的时间长。"

以下是移动设备和桌面设备上首次绘制时间中间值的这些变体明细,按 Service Worker 状态分组:

首次绘制所用的中位数(毫秒)
Service Worker 状态 桌面设备 移动设备
操控了 583 1634
支持(不受控制) 912 1933

虽然构建这些分布可视化所需的时间和精力比在 Google Analytics(分析)中创建自定义报告要多一些,但与单纯的平均值相比,我们可以更清楚地了解 Service Worker 对网站性能的影响。

Service Worker 的其他影响

除了性能影响之外,Service Worker 还会以多种其他可通过 Google Analytics(分析)衡量的方式影响用户体验。

离线访问

Service Worker 可让用户在离线状态下与您的网站进行交互,虽然某种离线支持对任何 Progressive Web App 来说可能都很重要,但对您而言,确定它的重要性在很大程度上取决于离线使用情况。但我们如何进行衡量呢?

向 Google Analytics(分析)发送数据需要有互联网连接,但不要求在互动发生的确切时间发送数据。Google Analytics(分析)支持在事后通过指定时间偏移量(通过 qt 参数)发送互动数据。

在过去的两年里,IOWA 一直在使用 Service Worker 脚本,该脚本能够在用户离线时检测 Google Analytics(分析)中失败的命中,并在稍后通过 qt 参数重放这些命中。

为了跟踪用户是处于在线还是离线状态,我们创建了名为“在线”的自定义维度并将其值设为 navigator.onLine,然后监听 onlineoffline 事件,并相应地更新维度。

此外,为了解用户在使用 IOWA 期间离线的情况有多常见,我们创建了一个细分,以定位到至少进行过一次线下互动的用户。原来,这几乎占用户总数的 5%。

推送通知

Service Worker 可让用户选择接收推送通知。在爱荷华州,用户会在其日程安排中的课程即将开始时收到通知。

与任何形式的通知一样,在为用户提供价值和让用户感到厌烦之间找到平衡很重要。为了更好地了解所发生的情况,请务必跟踪用户是否选择接收这些通知、他们是否会在到达时与通知互动,以及之前选择接收的任何用户是否改变了偏好设置和选择退出。

在爱荷华州,我们仅发送与用户的个性化时间表相关的通知,而这种通知只有已登录用户才能创建。这限制了可能向已登录用户发送通知的用户群体(通过名为“已登录”的自定义维度进行跟踪),这些用户的浏览器支持推送通知(通过另一个名为“通知权限”的自定义维度进行跟踪)。

以下报告基于“用户”指标和我们的“通知权限”自定义维度,并按曾在某个时间点登录过且所用浏览器支持推送通知的用户进行细分。

很高兴看到超过一半的已登录用户选择接收推送通知。

应用安装横幅

如果进度 Web 应用符合条件,并且用户经常使用,系统可能会向用户显示应用安装横幅,提示他们将该应用添加到主屏幕。

在爱荷华州,我们使用以下代码跟踪了向用户显示这些提示的频率(以及是否接受这些提示):

window.addEventListener('beforeinstallprompt', function(event) {
  // Tracks that the user saw a prompt.
  ga('send', 'event', {
    eventCategory: 'installprompt',
    eventAction: 'fired'
  });

  event.userChoice.then(function(choiceResult) {
    // Tracks the users choice.
    ga('send', 'event', {
      eventCategory: 'installprompt',
      // `choiceResult.outcome` will be 'accepted' or 'dismissed'.
      eventAction: choiceResult.outcome,
      // `choiceResult.platform` will be 'web' or 'android' if the prompt was
      // accepted, or '' if the prompt was dismissed.
      eventLabel: choiceResult.platform
    });
  });
});

在看到应用安装横幅的用户中,大约 10% 的用户选择将其添加到自己的主屏幕。

跟踪方面可能改进的方面(下次提供)

我们今年从 IOWA 收集的分析数据非常宝贵。但事后分析总是会带来漏洞和机会,以便下次有改进空间。完成今年的分析后,下面是希望我们改变的两件事情,希望实施类似策略的读者可以考虑以下两点:

1. 跟踪更多与加载体验相关的事件

我们跟踪了多个与某项技术指标(例如 HTMLImportsLoadedWebComponentsReady 等)对应的事件,但由于很大程度的加载是异步完成的,因此这些事件的触发点并不一定对应整体加载体验中的某个特定时刻。

我们未跟踪(但希望有如此)的主要与加载相关的事件是启动画面消失且用户可以看到网页内容的时间点。

2. 将分析客户端 ID 存储在 IndexedDB 中

默认情况下,analytics.js 会将客户端 ID 字段存储在浏览器的 Cookie 中;遗憾的是,Service Worker 脚本无法访问 Cookie。

在我们尝试实现通知跟踪时,这给我们带来了问题。我们希望在每次向用户发送通知时都通过 Measurement Protocol 从 Service Worker 发送一个事件,然后在用户点击该通知并返回应用后跟踪该通知的再互动成功与否。

虽然我们一般可以通过 utm_source 广告系列参数来跟踪通知的成效,但无法将特定的再互动会话与特定用户联系起来。

为解决这一限制,我们可以通过 IndexedDB 将客户端 ID 存储在跟踪代码中,然后 Service Worker 脚本就可以访问该值了。

3. 让 Service Worker 报告在线/离线状态

通过检查 navigator.onLine,您可以了解浏览器能否连接到路由器或局域网,但不一定能判断用户是否有真实连接。由于我们的离线分析 Service Worker 脚本只是重放失败的命中(没有修改或将其标记为失败),因此我们很可能漏报了离线使用情况。

将来,我们应该同时跟踪 navigator.onLine 的状态以及服务工作器是否因初始网络故障而重放命中。这样一来,我们就能更准确地了解真实的离线使用情况。

总结

本案例研究表明,使用 Service Worker 的确提高了 Google I/O Web 应用在各种浏览器、网络和设备上的加载性能。研究还表明,当查看各种浏览器、网络和设备中的加载数据分布情况时,可以深入了解此技术如何处理实际情况,并发现可能没有预料到的性能特征。

以下是 IOWA 研究的一些要点:

  • 平均而言,对于新访问者和回访者,使用 Service Worker 控制页面时,网页加载速度比不使用 Service Worker 时快得多。
  • 对由一个 Service Worker 控制的页面的访问,这些页面在许多用户中几乎可以即时加载。
  • 处于非活跃状态的 Service Worker 需要一些时间才能启动。不过,处于非活跃状态的 Service Worker 仍优于不使用 Service Worker。
  • 处于非活跃状态的 Service Worker 在移动设备上的启动时间比桌面设备长。

虽然在一个特定应用程序中观察到的性能提升通常对向更广泛的开发者群体报告很实用,但请务必注意,这些结果与 IOWA 网站类型(活动网站)和 IOWA 受众类型(大多数是开发者)有关。

如果您要在应用中实现 Service Worker,请务必实现自己的测量策略,以便评估自己的性能并防止将来出现回归问题。如果这样做,请分享您的结果,以便所有人都能受益!

脚注

  1. 将我们的 Service Worker 缓存实现的性能与仅使用 HTTP 缓存的网站的性能进行比较并不完全公平。由于我们针对 Service Worker 优化 IOWA,因此我们没有花太多时间针对 HTTP 缓存进行优化。如果我们这样设置,结果可能会有所不同。如需详细了解如何针对 HTTP 缓存优化您的网站,请参阅高效优化内容
  2. 根据您网站的样式和内容加载方式,浏览器有可能会在内容或样式可用之前便进行绘制。在这种情况下,firstpaint 可能对应于空白屏幕。如果您使用 firstpaint,请务必确保它对应于网站资源加载过程中的某个有意义的时间点。
  3. 从技术层面来讲,我们可以发送计时命中(默认为非互动)来捕获此类信息,而不是事件。事实上,计时命中是专门添加到 Google Analytics(分析)中来跟踪此类加载指标的;但是,计时命中在处理时会被大量抽样,因此它们的值不能在细分中使用。鉴于这些当前的局限性,非互动事件仍然更适合您。
  4. 要更好地了解在 Google Analytics(分析)中为自定义维度指定的范围,请参阅 Google Analytics(分析)帮助中心的自定义维度部分。此外,了解 Google Analytics(分析)数据模型也很重要,它由用户、会话和互动(命中)组成。要了解详情,请观看 Google Analytics(分析)学院中有关 Google Analytics(分析)数据模型的课程
  5. 这并未考虑在加载事件发生后延迟加载的资源。