从 Workbox v5 迁移到 v6

本指南重点介绍了 Workbox v6 中引入的破坏性更改,并举例说明了从 Workbox v5 升级时需要进行哪些更改。

破坏性更改

workbox-core

workbox-core 中的 skipWaiting() 方法将不再添加 install 处理程序,相当于仅调用 self.skipWaiting()。

从现在开始,请改用 self.skipWaiting(),因为 skipWaiting() 可能会在 Workbox v7 中移除。

workbox-precaching

  • 与 HTTP 重定向对应的网址的跨域 HTML 文档无法再用于满足 workbox-precaching 的导航请求。这种情况通常不常见。
  • 现在,在查找给定请求的预缓存响应时,workbox-precaching 会忽略 fbclid 网址查询参数。
  • PrecacheController 构造函数现在接受具有特定属性的对象作为其形参,而不是字符串。此对象支持以下属性:cacheName(与 v5 中传递给构造函数的字符串用途相同)、plugins(取代 v5 中的 addPlugins() 方法)和 fallbackToNetwork(取代 v5 中传递给 createHandler() 和 `createHandlerBoundToURL() 的类似选项)。
  • PrecacheController 的 install() 和 activate() 方法现在仅接受一个参数,该参数应分别设置为对应的 InstallEvent 或 ActivateEvent。
  • addRoute() 方法已从 PrecacheController 中移除。取而代之的是,您可以使用新的 PrecacheRoute 类创建路由,然后进行注册。
  • precacheAndRoute() 方法已从 PrecacheController 中移除。(它仍然作为 workbox-precaching 模块导出的静态辅助方法存在。)它已被移除,因为可以使用 PrecacheRoute 代替。
  • createMatchCalback() 方法已从 PrecacheController 中移除。您可以改用新的 PrecacheRoute。
  • createHandler() 方法已从 PrecacheController 中移除。PrecacheController 对象的 strategy 属性可用于处理请求。
  • createHandler() 静态导出已从 workbox-precaching 模块中移除。开发者应改为构建 PrecacheController 实例并使用其 strategy 属性。
  • 现在,向 precacheAndRoute() 注册的路由是“真实”路由,它在后台使用 workbox-routing 的 Router 类。如果您交错调用 registerRoute() 和 precacheAndRoute(),这可能会导致路由的评估顺序发生变化。

workbox-routing

setDefaultHandler() 方法现在接受可选的第二个参数,该参数对应于它所应用到的 HTTP 方法,默认为 'GET'。

  • 如果您使用 setDefaultHandler(),并且您的所有请求都是 GET,则无需进行任何更改。
  • 如果您有任何非 GET 请求(POST、PUT 等),setDefaultHandler() 将不再导致这些请求匹配。

build 配置

workbox-build 和 workbox-cli 中 getManifest 和 injectManifest 模式的 mode 选项原本不打算提供支持,现已移除。这不适用于 workbox-webpack-plugin,因为其 InjectManifest 插件支持 mode。

构建工具需要 Node.js v10 或更高版本

workbox-webpack-plugin、workbox-build 或 workbox-cli 不再支持 v10 之前的 Node.js 版本。如果您运行的 Node.js 版本低于 v8,请将运行时更新到受支持的版本。

新改进

workbox-strategies

Workbox v6 为第三方开发者引入了一种定义自己的 Workbox 策略的新方法。这可确保第三方开发者能够以完全满足其需求的方式扩展 Workbox。

新的 Strategy 基类

在 v6 中,所有 Workbox 策略类都必须扩展新的 Strategy 基类。所有内置策略都已重写,以支持此功能。

Strategy 基类主要负责两项工作:

  • 调用所有策略处理程序通用的插件生命周期回调(例如,在它们启动、响应和结束时)。
  • 创建一个“处理程序”实例,该实例可以管理策略正在处理的每个单独请求的状态。

新的“处理程序”类

我们之前让内部模块调用 fetchWrapper 和 cacheWrapper,顾名思义,它们会封装各种提取和缓存 API,并将其生命周期与钩子关联。这是目前允许插件运行的机制,但并未向开发者公开。

新的“处理程序”类 StrategyHandler 将公开这些方法,以便自定义策略可以调用 fetch() 或 cacheMatch(),并自动调用添加到策略实例中的所有插件。

此类还使开发者能够添加自己的自定义生命周期回调,这些回调可能特定于其策略,并且它们将“直接”与现有插件接口配合使用。

新插件生命周期状态

在 Workbox v5 中,插件是无状态的。这意味着,如果对 /index.html 的请求同时触发了 requestWillFetch 和 cachedResponseWillBeUsed 回调,这两个回调将无法相互通信,甚至无法知道它们是由同一请求触发的。

在 v6 中,所有插件回调也会传递一个新的 state 对象。此状态对象对于此特定插件对象和此特定策略调用(即对 handle() 的调用)是唯一的。这样,开发者就可以编写插件,其中一个回调可以根据同一插件中另一个回调的操作有条件地执行某些操作(例如,计算运行 requestWillFetch 与 fetchDidSucceed 或 fetchDidFail 之间的时间差)。

新的插件生命周期回调

添加了新的插件生命周期回调,以便开发者充分利用插件生命周期状态:

  • handlerWillStart:在任何处理脚本逻辑开始运行之前调用。此回调可用于设置初始处理程序状态(例如记录开始时间)。
  • handlerWillRespond:在策略 handle() 方法返回响应之前调用。此回调可用于在将响应返回给路由处理程序或其他自定义逻辑之前对其进行修改。
  • handlerDidRespond:在策略的 handle() 方法返回响应后调用。此回调可用于记录任何最终响应详细信息,例如在其他插件进行更改之后。
  • handlerDidComplete:在通过调用此策略添加到事件的所有延长生命周期 promise 均已解决后调用。此回调可用于报告任何需要等待处理程序完成才能计算的数据(例如缓存命中状态、缓存延迟时间、网络延迟时间)。
  • handlerDidError:如果处理程序无法从任何来源提供有效响应,则会调用此方法。此回调可用于提供“后备”内容,以替代网络连接错误。

实现自己的自定义策略的开发者不必担心自行调用这些回调;这一切都由新的 Strategy 基类处理。

更准确的处理程序 TypeScript 类型

各种回调方法的 TypeScript 定义已标准化。这应该会为使用 TypeScript 并编写自己的代码来实现或调用处理程序的开发者带来更好的体验。

workbox-window

新增了 messageSkipWaiting() 方法

workbox-window 模块中添加了新方法 messageSkipWaiting(),以简化指示“等待中”的 Service Worker 激活的过程。这带来了一些改进:

  • 它会使用事实上的标准消息正文 {type: 'SKIP_WAITING'} 调用 postMessage(),由 Workbox 生成的 Service Worker 会检查该正文,以触发 skipWaiting()。
  • 它会选择正确的“等待”Service Worker 来发布此消息,即使它与注册 workbox-window 的 Service Worker 不同。

移除了“外部”事件,改为使用 isExternal 属性

workbox-window 中的所有“外部”事件均已移除,取而代之的是 isExternal 属性设置为 true 的“正常”事件。这样,关注这种区别的开发者仍然可以检测到它,而不需要知道这种区别的开发者可以忽略该属性。

更简洁的“为用户提供页面重新加载功能”方案

由于上述两项更改,“为用户提供页面重新加载”方案可以简化:

// v6:
<script type="module">
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.0.0-alpha.1/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');

    const showSkipWaitingPrompt = () => {
      // This assumes a hypothetical createUIPrompt() method with
      // onAccept and onReject callbacks:
      const prompt = createUIPrompt({
        onAccept: () => {
          wb.addEventListener('controlling', () => {
            window.location.reload();
          });

          // This will postMessage() to the waiting service worker.
          wb.messageSkipWaiting();
        },

        onReject: () => {
          prompt.dismiss();
        },
      });
    };

    // Listening for externalwaiting is no longer needed.
    wb.addEventListener('waiting', showSkipWaitingPrompt);
    wb.register();
  }
</script>

workbox-routing

一个新的布尔值参数 sameOrigin 会传递给 workbox-routing 中使用的 matchCallback 函数。如果请求针对的是同源网址,则该值设置为 true,否则设置为 false。

这简化了一些常见的样板代码:

// In v5:
registerRoute(
  ({url}) =>
    url.origin === self.location.origin && url.pathname.endsWith('.png'),
  new StaleWhileRevalidate({cacheName: 'local-png'})
);

// In v6:
registerRoute(
  ({sameOrigin, url}) => sameOrigin && url.pathname.endsWith('.png'),
  new StaleWhileRevalidate({cacheName: 'local-png'})
);

workbox-expiration 中的 matchOptions

您现在可以在 workbox-expiration 中设置 matchOptions,然后该值将作为 CacheQueryOptions 传递给底层 cache.delete() 调用。(大多数开发者无需执行此操作。)

workbox-precaching

使用 workbox-strategies

workbox-precaching 已重写为以 workbox-strategies 为基础。这应该不会导致任何破坏性更改,并且应该会使这两个模块在访问网络和缓存的方式上实现更好的长期一致性。

预缓存现在会逐个处理条目,而不是批量处理

workbox-precaching 已更新,因此一次只会请求和缓存预缓存清单中的一个条目,而不是尝试一次性请求和缓存所有条目(让浏览器自行决定如何进行限制)。

这应该会降低预缓存时发生 net::ERR_INSUFFICIENT_RESOURCES 错误的可能性,并且应该会减少预缓存与 Web 应用发出的并发请求之间的带宽争用。

PrecacheFallbackPlugin 可让您更轻松地实现离线回退

workbox-precaching 现在包含一个 PrecacheFallbackPlugin,用于实现 v6 中添加的新 handlerDidError 生命周期方法。

这样,当无法提供响应时,就可以轻松地将预缓存的网址指定为给定策略的“后备”。该插件会负责为预缓存的网址正确构建正确的缓存键,包括所需的任何修订版本参数。

以下示例展示了如何在 NetworkOnly 策略无法为导航请求生成响应时,使用该方法返回预缓存的 /offline.html,即显示自定义离线 HTML 页面:

import {PrecacheFallbackPlugin, precacheAndRoute} from 'workbox-precaching';
import {registerRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

// Ensure that /offline.html is part of your precache manifest!
precacheAndRoute(self.__WB_MANIFEST);

registerRoute(
  ({request}) => request.mode === 'navigate',
  new NetworkOnly({
    plugins: [
      new PrecacheFallbackPlugin({
        fallbackURL: '/offline.html',
      }),
    ],
  })
);

precacheFallback 项在运行时缓存

如果您使用 generateSW 为您创建 Service Worker,而不是手动编写 Service Worker,则可以使用 runtimeCaching 中的新 precacheFallback 配置选项来实现相同的目的:

{
  // ... other generateSW config options...
  runtimeCaching: [{
    urlPattern: ({request}) => request.mode === 'navigate',
    handler: 'NetworkOnly',
    options: {
      precacheFallback: {
        // This URL needs to be included in your precache manifest.
        fallbackURL: '/offline.html',
      },
    },
  }],
}

获取帮助

我们预计大多数迁移过程都会很简单。如果您遇到本指南中未涵盖的问题,请在 GitHub 上提交问题,告知我们。