后台提取简介

Jake Archibald
Jake Archibald

2015 年,我们推出了后台同步功能,该功能允许 Service Worker 将工作推迟到用户连接到网络时再执行。也就是说,用户可以输入 点击“发送”,即可离开网站 没有网络连接。

这是一项有用的功能,但要求 Service Worker 在 提取。这对于发送消息等小工作来说是个问题 浏览器终止 Service Worker 的时间过长,否则将给用户隐私带来风险, 电量。

因此,如果您需要下载可能需要很长时间的内容,例如电影、播客或 游戏关卡这正是后台提取的用武之地。

从 Chrome 74 开始,系统默认提供后台提取功能。

以下快速演示了两分钟,展示了与使用后台提取相比的操作传统状态:

亲自试用演示版浏览代码

工作原理

后台提取的运作方式如下:

  1. 您告知浏览器在后台执行一组提取。
  2. 浏览器提取这些内容,并向用户显示进度。
  3. 提取完成或失败后,浏览器会打开您的 Service Worker 并触发一个事件 告诉你发生了什么您可以在此处决定如何处理这些响应(如果有)。

如果用户在第 1 步之后关闭您网站上的网页,也没关系,下载会继续进行。因为 提取高度可见且易于取消,不存在时间过长的隐私问题 后台同步任务。由于 Service Worker 不会持续运行,因此就无需担心 可能会滥用系统,例如在后台挖掘比特币。

在某些平台(如 Android)上,浏览器可能会在步骤 1 之后关闭,因为 浏览器可以将抓取操作交给操作系统处理。

如果用户在离线状态下开始下载,或在下载过程中进入离线状态,则后台 抓取操作暂停,稍后恢复。

API

功能检测

与任何新功能一样,您需要检测浏览器是否支持该功能。对于后台提取 简单明了,如下所示:

if ('BackgroundFetchManager' in self) {
  // This browser supports Background Fetch!
}

启动后台提取

主 API 会挂起 Service Worker 注册, 因此,请务必先注册一个 Service Worker。然后,执行以下操作:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch('my-fetch', ['/ep-5.mp3', 'ep-5-artwork.jpg'], {
    title: 'Episode 5: Interesting things.',
    icons: [{
      sizes: '300x300',
      src: '/ep-5-icon.png',
      type: 'image/png',
    }],
    downloadTotal: 60 * 1024 * 1024,
  });
});

backgroundFetch.fetch 采用三个参数:

参数
id string
此项后台提取的唯一标识。

如果 ID 与现有背景匹配,backgroundFetch.fetch 将拒绝 提取。

requests Array<Request|string>
要提取的内容。字符串将被视为网址,并转换为 Request(通过 new Request(theString))。

您可以从其他来源提取内容,前提是相应资源允许通过 CORS

注意:Chrome 目前不支持 需要进行 CORS 预检。

options 该对象可能包含以下内容:
options.title string
与进度一起显示的浏览器标题。
options.icons Array<IconDefinition>
一组包含 `src`、`size` 和 `type` 的对象。
options.downloadTotal number
响应正文(解压缩后)的总大小。

虽然此为可选项,但我们强烈建议您提供。它用于识别 用户下载的内容大小,以及提供进度信息。如果您没有提供 这样,浏览器就会告知用户尺寸未知, 可能会取消下载。

如果后台提取下载次数超过此处给定的数值,将会取消下载。时间是 如果下载内容小于 downloadTotal,则完全没有问题,因此,如果您不 确保总下载次数是多少,谨慎行事最好。

backgroundFetch.fetch 会返回使用 BackgroundFetchRegistration 进行解析的 promise。我将 稍后会进行详细介绍如果用户选择停用下载,或某 提供的参数中有个无效。

通过为单次后台提取提供多个请求,您可以将逻辑上的内容进行合并, 只为用户提供一个东西例如,一部电影可能会拆分成上千个资源(通常 MPEG-DASH), 还会提供图片等其他资源一个游戏的关卡可以分布在 JavaScript、图片和音频资源。但对用户来说,则只是“电影”或“关卡”。

获取现有的后台提取

您可以像下面这样获取现有的后台提取:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});

...通过传递所需的后台提取的 id。如果不存在,get 会返回 undefined。 具有该 ID 的有效后台提取。

后台提取被视为“活跃”它从注册到成功为止 或被中止

您可以使用 getIds 获取所有活跃后台提取的列表:

navigator.serviceWorker.ready.then(async (swReg) => {
  const ids = await swReg.backgroundFetch.getIds();
});

后台提取注册

BackgroundFetchRegistration(上述示例中的 bgFetch)包含以下内容:

属性
id string
后台提取的 ID。
uploadTotal number
要发送到服务器的字节数。
uploaded number
已成功发送的字节数。
downloadTotal number
注册后台提取时提供的值,或 零。
downloaded number
成功接收的字节数。

此值可能会降低。例如,如果连接断开, 已恢复,在这种情况下,浏览器会从头开始重新抓取该资源。

result

以下项之一:

  • "" - 后台提取处于活动状态,因此还没有结果。
  • "success" - 后台提取成功。
  • "failure" - 后台提取失败。只有在 后台提取完全失败,因为在浏览器中无法重试/恢复。
failureReason

以下项之一:

  • "" - 后台提取未失败。
  • "aborted" - 后台提取被用户取消,或者 已调用 abort()
  • "bad-status" - 其中一个响应处于错误状态,例如404。
  • "fetch-error" - 某项提取操作因某种其他原因(例如)失败 CORS、MIX、无效的部分响应或 无法重试。
  • "quota-exceeded" - 在后台达到了存储空间配额 提取。
  • "download-total-exceeded" - 所提供的“downloadTotal”是 超出上限。
recordsAvailable boolean
能否访问底层请求/响应?

一旦此属性为 false,便无法使用 matchmatchAll

方法
abort() 返回 Promise<boolean>
中止后台提取。

如果成功中止提取,返回的 promise 会解析为 true。

matchAll(request, opts) 返回 Promise<Array<BackgroundFetchRecord>>
获取请求 和响应。

这里的参数与 缓存 API。不使用参数的调用会针对所有记录返回一个 promise。

详见下文说明。

match(request, opts) 返回 Promise<BackgroundFetchRecord>
同上,但解析为 匹配。
事件
progress 在以下任一项时触发:uploadeddownloadedresultfailureReason 项更改。

跟踪进度

这可以通过 progress 事件实现。请注意,downloadTotal 是您 或 0(如果未提供值)。

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(bgFetch.downloaded / bgFetch.downloadTotal * 100);
  console.log(`Download progress: ${percent}%`);
});

获取请求和响应

bgFetch.match('/ep-5.mp3').then(async (record) => {
  if (!record) {
    console.log('No record found');
    return;
  }

  console.log(`Here's the request`, record.request);
  const response = await record.responseReady;
  console.log(`And here's the response`, response);
});

record 是一个 BackgroundFetchRecord,如下所示:

属性
request Request
提供的请求。
responseReady Promise<Response>
提取的响应。

响应在 promise 之后,因为可能尚未收到。承诺 如果提取失败,则会拒绝

Service Worker 事件

事件
backgroundfetchsuccess 已成功提取所有内容。
backgroundfetchfailure 一项或多项提取失败。
backgroundfetchabort 一项或多项提取失败。

这只有在您需要清理相关数据时才能真正派上用场。

backgroundfetchclick 用户点击了下载进度界面。

事件对象具有以下特征:

属性
registration BackgroundFetchRegistration
方法
updateUI({ title, icons }) 可让您更改最初设置的标题/图标。这是可选操作, 必要时提供更多背景信息。在 backgroundfetchsuccessbackgroundfetchfailure 事件。

对成功/失败做出反应

我们已经见过 progress 事件,但该事件只有在用户打开了 。后台提取的主要好处是,在用户离开 甚至关闭浏览器。

如果后台提取成功完成,您的 Service Worker 将收到 backgroundfetchsuccess 事件,而 event.registration 将是后台提取注册。

此事件之后,提取的请求和响应将无法再访问,因此,如果您想 可以将其移到诸如 cache API 之类的地方。

与大多数 Service Worker 事件一样,使用 event.waitUntil,以便 Service Worker 知道 。

例如,在您的 Service Worker 中:

addEventListener('backgroundfetchsuccess', (event) => {
  const bgFetch = event.registration;

  event.waitUntil(async function() {
    // Create/open a cache.
    const cache = await caches.open('downloads');
    // Get all the records.
    const records = await bgFetch.matchAll();
    // Copy each request/response across.
    const promises = records.map(async (record) => {
      const response = await record.responseReady;
      await cache.put(record.request, response);
    });

    // Wait for the copying to complete.
    await Promise.all(promises);

    // Update the progress notification.
    event.updateUI({ title: 'Episode 5 ready to listen!' });
  }());
});

但失败后可能会出现单 404 错误,而该错误可能对您并不重要,因此, 仍然值得如上所述将一些响应复制到缓存中。

响应点击

显示下载进度和结果的界面是可点击的。backgroundfetchclick事件 Service Worker 则可让您对此作出反应。如上所述,背景为 event.registration 提取注册信息。

此事件的常见操作是打开一个窗口:

addEventListener('backgroundfetchclick', (event) => {
  const bgFetch = event.registration;

  if (bgFetch.result === 'success') {
    clients.openWindow('/latest-podcasts');
  } else {
    clients.openWindow('/download-progress');
  }
});

其他资源

更正:本文先前版本误将后台提取称为“网络标准”。该 API 目前已进入标准轨道,规范以社区小组报告草稿的形式在 WICG 中提供。