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

至少从性能的角度来看,Service Worker 最显著的优势之一是能够主动控制资源的缓存。对于回访者来说,可以缓存所有必要资源的 Web 应用应该会明显加快加载速度。但对于真实用户而言,这些成果是什么样的呢?那么,如何衡量呢?

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

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

我们先从问题开始

每当您在网站或应用中实施 Google Analytics 时,都必须先从将要收集的数据中确定想要解答的问题。

虽然我们有好几个问题需要回答,但在本案例研究中,我们只重点介绍其中两个比较有趣的问题。

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

由于浏览器可以缓存请求并在重复访问时即时提供请求,因此我们预计回访者的页面加载速度会比新访问者更快。

Service Worker 提供备用缓存功能,让开发者能够精确控制缓存的具体内容和方式。在爱荷华,我们优化了 Service Worker 的实现,以便缓存每个资源,使回访者可以完全离线使用应用。

但是,这种努力是否比浏览器已默认设置的操作更好?如果是,那该如何改进呢?1

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

换言之,网站加载速度如何,不考虑传统网页加载指标衡量的实际加载时间?

回答关于体验感受的问题显然不是一件容易的事,任何指标都无法完全代表这种主观情绪。尽管如此,肯定有一些指标优于其他指标,因此选择适当的指标非常重要。

选择合适的指标

默认情况下,Google Analytics 会跟踪 1% 的网站访问者的网页加载时间(通过 Navigation Timing API),并通过“平均网页加载时间。

平均在回答第一个问题时,“网页加载时间”是一个不错的指标,但在回答第二个问题时则不是一个特别好的指标。首先,load 事件并不一定对应于用户可以实际与应用互动的时刻。此外,加载时间完全相同的两个应用的加载体验可能大相径庭。例如,对于带有启动画面或加载指示器的网站,用户感觉其加载速度比显示空白页几秒钟的网站要快得多。

在爱荷华州,我们展示了启动画面倒计时动画(我认为),该动画非常成功地为用户提供娱乐体验,同时应用的其余部分在后台加载。因此,跟踪启动画面出现的时间就是一种更加有意义的方法,用于衡量用户感知的加载性能。我们选择了“首次渲染时间”指标来获取该值。

确定了要解答的问题并确定了有助于解答这些问题的指标后,我们便实施了 Google 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');

这两个命令可以跟踪访问您网站的用户访问过的网页,但仅限于此。如果您想跟踪其他用户互动,则必须自行完成。

对于爱荷华,我们还想跟踪另外两个方面:

  • 从网页首次开始加载到像素在屏幕上显示所用的时间。
  • 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 自行创建自定义维度并随意定义它们。

对于 IOWA,我们创建了一个名为 Service Worker Status 的自定义维度,并将其范围设置为 hit(即每次互动)。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 Status 维度,我们便可以在报告任何指标时使用该维度。

如您所见,IOWA 的所有网页浏览中有近 85% 来自支持 Service Worker 的浏览器。

成果:解答我们的疑问

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

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

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

我们选择的维度是:

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

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

如您所见,在由 Service Worker 控制的情况下,对应用的访问的加载速度要比非受控访问的加载速度快得多,即使是那些可能缓存了网页的大部分资源的回访用户也是如此。还有一件有趣的是,平均而言,使用 Service Worker 的移动设备访问者比新的桌面设备访问者更快的加载速度。

“...由 Service Worker 控制时对我们应用的访问比非控制访问的加载速度要快得多...”

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

平均网页加载时间(桌面设备)
Service Worker 状态 用户类型 平均网页加载时间(毫秒) 样本规模
操控了 回访者 2568 30860
支持 回访者 3612 1289
支持 新访者 4,664,000 21991
平均网页加载时间(移动设备)
Service Worker 状态 用户类型 平均网页加载时间(毫秒) 样本规模
操控了 回访者 3,760 8162
支持 回访者 4843 676
支持 新访者 6,158 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:维度 2 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 允许用户在离线时与您的网站进行交互,虽然某种离线支持可能对任何渐进式 Web 应用都至关重要,但能否确定它对您案例的重要程度在很大程度上取决于离线使用的情况。但如何衡量呢?

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

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

为了跟踪用户在线还是离线,我们创建了一个名为 Online 的自定义维度,并将其值设为 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% 选择将其添加到主屏幕。

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

我们今年从爱荷华州收集的分析数据非常宝贵。但事后洞察总是会带来光明洞,并抓住下一次改进的机会。在完成今年的分析后,我希望我们做出以下两件事,希望采用类似策略的读者可以考虑:

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

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

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

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

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

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

虽然我们能够通过 utm_source 广告系列参数来跟踪通知的总体效果,但无法将特定的再互动会话与特定用户关联起来。

我们可以通过以下方法解决这一限制:通过 IndexedDB 将 Client-ID 存储在我们的跟踪代码中,然后 Service Worker 脚本就可以访问该值。

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

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

今后,我们应同时跟踪 navigator.onLine 的状态以及命中是否因初始网络故障而被 Service Worker 重放。这样,我们就能更准确地了解真实的离线使用情况。

总结

本案例研究表明,使用 Service Worker 确实提高了 Google I/O 网页应用在各种浏览器、网络和设备上的加载性能。实验还显示,当您查看各种浏览器、网络和设备的负载数据分布情况时,可以更清楚地了解这项技术如何处理实际场景,并发现您可能没有预料到的性能特征。

以下是从爱荷华州调查研究得出的一些关键要点:

  • 平均而言,对于新访者和回访者而言,与没有 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. 这没有考虑到加载事件后延迟加载的资源。