工作区预缓存

Service Worker 的一个功能是能够在安装 Service Worker 时将一组文件保存到缓存。这通常称为“预缓存”,因为您是在使用的 Service Worker 之前缓存内容。

这样做的主要原因是,它使开发者能够控制缓存,这意味着他们可以确定文件的缓存时间和时长,并且可以在不连接网络的情况下将文件提供给浏览器,这意味着可用于创建离线工作的 Web 应用。

Workbox 可简化 API 并确保高效下载资源,从而免去了预缓存的许多繁重工作。

Workbox 预缓存的工作原理

首次加载 Web 应用时,workbox-precaching 将查看您要下载的所有资源,移除所有重复项,然后连接相关的 Service Worker 事件以下载并存储资源。已包含版本控制信息(例如内容哈希)的网址会被用作缓存键,而不会被进一步修改。对于不包含版本控制信息的网址,其缓存键中附加了一个额外的网址查询参数,表示 Workbox 在构建时生成的内容的哈希值。

workbox-precaching 会在 Service Worker 的 install 事件期间执行所有这些操作。

当用户稍后再次访问您的 Web 应用时,并且您有一个具有不同预缓存资源的新 Service Worker 时,workbox-precaching 将查看新列表,并根据修订版本的修订确定哪些资源是全新的,哪些现有资源需要更新。在新 Service Worker 的 install 事件期间,任何新资源或更新修订版本都将添加到缓存中。

在触发其 activate 事件之前,系统不会使用以下新的 Service Worker 响应请求。在 activate 事件中,workbox-precaching 将检查当前网址列表中已不存在的任何已缓存资源,并从缓存中移除这些资源。

每次安装并激活 Service Worker 时,workbox-precaching 都会执行这些步骤,以确保用户拥有最新的资源,并且仅下载已更改的文件。

提供预缓存响应

调用 precacheAndRoute()addRoute() 将创建与对预缓存网址的请求匹配的路由

此路由中使用缓存优先策略:系统将使用预缓存的响应,除非该缓存响应不存在(由于一些意外错误导致),在这种情况下,系统将改用网络响应。

调用 precacheAndRoute()addRoute() 的顺序很重要。您通常需要尽早在 Service Worker 文件中调用它,然后再使用 registerRoute() 注册任何其他路由。如果您先调用了 registerRoute(),并且该路由与传入请求匹配,则您在该额外路由中定义的任何策略都将用于响应,而不是 workbox-precaching 使用的缓存优先策略。

预缓存列表的说明

workbox-precaching 需要具有 urlrevision 属性的对象数组。此数组有时称为预缓存清单:

import {precacheAndRoute} from 'workbox-precaching';

precacheAndRoute([
  {url: '/index.html', revision: '383676'},
  {url: '/styles/app.0c9a31.css', revision: null},
  {url: '/scripts/app.0d5770.js', revision: null},
  // ... other entries ...
]);

此列表引用了一组网址,每个网址都有自己的“修订”信息。

对于上述示例中的第二个和第三个对象,revision 属性设置为 null。这是因为修改信息位于网址本身中,这通常是静态资源的最佳做法。

第一个对象 (/index.html) 会明确设置修订版本属性,该属性是自动生成的文件内容的哈希值。与 JavaScript 和 CSS 资源不同,HTML 文件通常不能在网址中包含修订信息,否则,只要网页内容发生变化,指向网络上的这些文件的链接就会中断。

通过将修订版本属性传递给 precacheAndRoute(),Workbox 可以知道文件何时发生更改并相应地进行更新。

Workbox 附带一些工具,可帮助您生成此列表:

  • workbox-build:这是一个可在 gulp 任务中使用或用作 npm 运行脚本的节点软件包。
  • workbox-webpack-plugin:webpack 用户可以使用此插件。
  • workbox-cli:我们的 CLI 还可用于生成资源列表并将其添加到您的 Service Worker。

针对预缓存文件的传入请求

workbox-precaching 的一项开箱即用功能是操纵传入的网络请求,以尝试匹配预缓存的文件。这适用于网络上的常见做法。

例如,对 / 的请求通常可以通过 /index.html 中的文件来满足。

下面列出了 workbox-precaching 默认执行的操作,以及如何更改该行为。

忽略网址参数

包含搜索参数的请求可被更改以移除特定值或移除所有值。

默认情况下,系统会移除以 utm_ 开头或与 fbclid 完全匹配的搜索参数,这意味着针对 /about.html?utm_campaign=abcd 的请求将通过 /about.html 的预缓存条目执行。

您可以使用 ignoreURLParametersMatching 忽略一组不同的搜索参数:

import {precacheAndRoute} from 'workbox-precaching';

precacheAndRoute(
  [
    {url: '/index.html', revision: '383676'},
    {url: '/styles/app.0c9a31.css', revision: null},
    {url: '/scripts/app.0d5770.js', revision: null},
  ],
  {
    // Ignore all URL parameters.
    ignoreURLParametersMatching: [/.*/],
  }
);

目录索引

默认情况下,以 / 结尾的请求将与末尾附加了 index.html 的条目进行匹配。这意味着,可以使用预缓存条目 /index.html 自动处理对 / 的传入请求。

您可将其更改为其他内容,也可以通过设置 directoryIndex 将其完全停用:

import {precacheAndRoute} from 'workbox-precaching';

precacheAndRoute(
  [
    {url: '/index.html', revision: '383676'},
    {url: '/styles/app.0c9a31.css', revision: null},
    {url: '/scripts/app.0d5770.js', revision: null},
  ],
  {
    directoryIndex: null,
  }
);

简洁网址

如果请求与预缓存不匹配,我们会在末尾添加 .html,以支持“干净”网址(也称为“漂亮”网址)。这意味着,像 /about 这样的请求将由 /about.html 的预缓存条目处理。

您可以通过设置 cleanUrls 来禁止此行为:

import {precacheAndRoute} from 'workbox-precaching';

precacheAndRoute([{url: '/about.html', revision: 'b79cd4'}], {
  cleanUrls: false,
});

自定义操作

如果要定义从传入请求到预缓存资产的自定义匹配,您可以使用 urlManipulation 选项来实现。这应该是一个返回可能匹配项数组的回调。

import {precacheAndRoute} from 'workbox-precaching';

precacheAndRoute(
  [
    {url: '/index.html', revision: '383676'},
    {url: '/styles/app.0c9a31.css', revision: null},
    {url: '/scripts/app.0d5770.js', revision: null},
  ],
  {
    urlManipulation: ({url}) => {
      // Your logic goes here...
      return [alteredUrlOption1, alteredUrlOption2];
    },
  }
);

高级用法

直接使用 PrecacheController

默认情况下,workbox-precaching 会为您设置 installactivate 监听器。对于熟悉 Service Worker 的开发者,如果您需要更多控制权,则可能不希望出现这种情况。

您可以直接使用 PrecacheController 将项添加到预缓存中、确定何时安装这些资源以及何时应执行清理,而不是使用默认导出。

import {PrecacheController} from 'workbox-precaching';

const precacheController = new PrecacheController();
precacheController.addToCacheList([
  {url: '/styles/example-1.abcd.css', revision: null},
  {url: '/styles/example-2.1234.css', revision: null},
  {url: '/scripts/example-1.abcd.js', revision: null},
  {url: '/scripts/example-2.1234.js', revision: null},
]);

precacheController.addToCacheList([{
  url: '/index.html',
  revision: 'abcd',
}, {
  url: '/about.html',
  revision: '1234',
}]);

self.addEventListener('install', (event) => {
  // Passing in event is required in Workbox v6+
  event.waitUntil(precacheController.install(event));
});

self.addEventListener('activate', (event) => {
  // Passing in event is required in Workbox v6+
  event.waitUntil(precacheController.activate(event));
});

self.addEventListener('fetch', (event) => {
  const cacheKey = precacheController.getCacheKeyForURL(event.request.url);
  event.respondWith(caches.match(cacheKey).then(...));
});

直接读取预缓存的资源

有时,您可能需要在 workbox-precaching 可以自动执行的路由上下文之外直接读取预缓存的资源。例如,您可能想要预缓存部分 HTML 模板,然后在构建完整响应时需要检索并使用这些模板。

一般情况下,您可以使用 Cache Storage API 获取预缓存的 Response 对象,但存在一个问题:调用 cache.match() 时需要使用的网址缓存键可能包含 workbox-precaching 自动创建和维护的版本控制参数。

如需获取正确的缓存键,您可以调用 getCacheKeyForURL() 并传入原始网址,然后使用结果对相应的缓存执行 cache.match()

import {cacheNames} from 'workbox-core';
import {getCacheKeyForURL} from 'workbox-precaching';

const cache = await caches.open(cacheNames.precache);
const response = await cache.match(getCacheKeyForURL('/precached-file.html'));

或者,如果您只需要预缓存的 Response 对象,则可以调用 matchPrecache(),它会自动使用正确的缓存键并在正确的缓存中搜索:

import {matchPrecache} from 'workbox-precaching';

const response = await matchPrecache('/precached-file.html');

清理旧的预缓存

大多数 Workbox 版本都会保持相同的预缓存数据格式,并且较旧版本的 Workbox 创建的预缓存通常可以按原样用于较新版本。但在极少数情况下,预缓存存储空间会发生一项破坏性更改,需要现有用户重新下载所有内容,这会导致之前预缓存的数据已过时。(Workbox v3 和 v4 版本之间发生了这样的变更。)

这些过时的数据应该不会影响正常操作,但确实会占用存储空间的总配额,而且明确删除这些数据会更利于用户。为此,您可以将 cleanupOutdatedCaches() 添加到 Service Worker,或者设置 cleanupOutdatedCaches: true(如果您使用 Workbox 的某个构建工具生成 Service Worker)。

使用子资源完整性

某些开发者在从网络中检索预缓存网址时,可能希望子资源完整性强制执行能够提供额外的保证。

您可以向预缓存清单中的任意条目添加一个名为 integrity 的可选属性。如果提供,则在构建用于填充缓存的 Request 时,它将用作 integrity。如果有不匹配,预缓存过程将失败。

确定哪些预缓存清单条目应具有 integrity 属性以及确定要使用的适当值不在 Workbox 构建工具的范围之内。相反,想要选择使用此功能的开发者应修改 Workbox 生成的预缓存清单,以自行添加适当的信息。Workbox 构建工具配置中的 manifestTransform 选项可为您提供帮助:

const ssri = require('ssri');

const integrityManifestTransform = (originalManifest, compilation) => {
  const warnings = [];
  const manifest = originalManifest.map(entry => {
    // If some criteria match:
    if (entry.url.startsWith('...')) {
      // This has to be a synchronous function call, for example:
      // compilation will be set when using workbox-webpack-plugin.
      // When using workbox-build directly, you can read the file's
      // contents from disk using, e.g., the fs module.
      const asset = compilation.getAsset(entry.url);
      entry.integrity = ssri.fromData(asset.source.source()).toString();

      // Push a message to warnings if needed.
    }
    return entry;
  });

  return {warnings, manifest};
};

// Then add manifestTransform: [integrityManifestTransform]
// to your Workbox build configuration.

类型

CleanupResult

属性

  • deletedCacheRequests

    字符串[]

InstallResult

属性

  • notUpdatedURLs

    字符串[]

  • updatedURLs

    字符串[]

PrecacheController

执行有效的资产预缓存。

属性

  • 构造函数

    void

    创建一个新的 PrecacheController。

    constructor 函数如下所示:

    (options?: PrecacheControllerOptions) => {...}

    • 选项

      PrecacheControllerOptions 可选

  • 策略

    策略

  • 启用

    void

    删除当前预缓存清单中不再存在的资产。从 Service Worker 的激活事件中调用此方法。

    注意:此方法会为您调用 event.waitUntil(),因此您无需在事件处理脚本中自行调用它。

    activate 函数如下所示:

    (event: ExtendableEvent) => {...}

    • event

      ExtendableEvent

  • addToCacheList

    void

    此方法会将项添加到预缓存列表中,从而移除重复项并确保信息有效。

    addToCacheList 函数如下所示:

    (entries: (string | PrecacheEntry)[]) => {...}

    • entries

      (字符串 | PrecacheEntry)[]

      要预缓存的条目数组。

  • createHandlerBoundToURL

    void

    返回一个函数,该函数在预缓存中查找 url(考虑修订信息),并返回相应的 Response

    createHandlerBoundToURL 函数如下所示:

    (url: string) => {...}

    • 网址

      string

      将用于查找 Response 的预缓存网址。

  • getCacheKeyForURL

    void

    返回用于存储指定网址的缓存键。如果该网址不带版本信息(例如“/index.html”),则缓存键将是原始网址并附加一个搜索参数。

    getCacheKeyForURL 函数如下所示:

    (url: string) => {...}

    • 网址

      string

      您要查找其缓存键的网址。

    • 返回

      string

      与原始网址的缓存键对应的版本化网址,如果未预缓存该网址,则为未定义的网址。

  • getCachedURLs

    void

    返回已由当前 Service Worker 预缓存的所有网址的列表。

    getCachedURLs 函数如下所示:

    () => {...}

    • 返回

      字符串[]

      预缓存的网址。

  • getIntegrityForCacheKey

    void

    getIntegrityForCacheKey 函数如下所示:

    (cacheKey: string) => {...}

    • cacheKey

      string

    • 返回

      string

      与缓存键关联的子资源完整性,如果未设置,则为未定义。

  • getURLsToCacheKeys

    void

    返回预缓存网址到相应缓存键的映射,并考虑网址的修订版本信息。

    getURLsToCacheKeys 函数如下所示:

    () => {...}

    • 返回

      地图<字符串>

      用于缓存键映射的网址。

  • 安装

    void

    预缓存新资产和更新后的资产。从 Service Worker 安装事件调用此方法。

    注意:此方法会为您调用 event.waitUntil(),因此您无需在事件处理脚本中自行调用它。

    install 函数如下所示:

    (event: ExtendableEvent) => {...}

    • event

      ExtendableEvent

  • matchPrecache

    void

    它可以直接替换 cache.match(),但存在以下差异:

    • 它知道预缓存的名称,并且只会签入该缓存。
    • 它允许您传入不带版本控制参数的“原始”网址,并且它会自动为该网址的当前有效修订版本查找正确的缓存键。

    例如,即使实际缓存键为 '/index.html?__WB_REVISION__=1234abcd'matchPrecache('index.html') 也会为当前处于活跃状态的 Service Worker 找到正确的预缓存响应。

    matchPrecache 函数如下所示:

    (request: string | Request) => {...}

    • request

      字符串 | 请求

      要在预缓存中查找的键(无需修改参数)。

    • 返回

      Promise<Response>

  • 预缓存

    void

    在 Service Worker 安装时,将项添加到预缓存列表中,移除所有重复项并将文件存储在缓存中。

    此方法可多次调用。

    precache 函数如下所示:

    (entries: (string | PrecacheEntry)[]) => {...}

PrecacheEntry

属性

  • 完整性

    字符串(可选)

  • 修订版本

    字符串(可选)

  • 网址

    string

PrecacheFallbackPlugin

PrecacheFallbackPlugin 允许您指定“离线回退”响应,以便在指定策略无法生成响应时使用。

为此,它会拦截 handlerDidError 插件回调并返回预缓存的响应,并自动考虑预期的修订版本参数。

除非您明确将 PrecacheController 实例传递给构造函数,否则系统将使用默认实例。一般来说,大多数开发者最终都会使用默认值。

属性

  • 构造函数

    void

    使用关联的 fallback网址 构造一个新的 PrecacheFallbackPlugin。

    constructor 函数如下所示:

    (config: object) => {...}

    • config

      对象

      • fallbackURL

        string

        预缓存的网址,在关联的策略无法生成响应时用作后备网址。

      • precacheController

PrecacheRoute

workbox-routing.Route 的子类,接受 workbox-precaching.PrecacheController 实例,并使用该实例匹配传入请求并处理从预缓存中提取响应。

属性

PrecacheRouteOptions

属性

  • cleanURLs

    布尔值 选填

  • directoryIndex

    字符串(可选)

  • ignoreURLParametersMatching

    RegExp[] 可选

  • urlManipulation

PrecacheStrategy

workbox-strategies.Strategy 实现,专门与 workbox-precaching.PrecacheController 配合使用,以缓存和提取预缓存的资源。

注意:创建 PrecacheController 时,系统会自动创建此类的实例;通常,您无需自行创建此类实例。

属性

  • 构造函数

    void

    constructor 函数如下所示:

    (options?: PrecacheStrategyOptions) => {...}

    • 选项

      PrecacheStrategyOptions 可选

  • cacheName

    string

  • fetchOptions

    RequestInit(可选)

  • matchOptions

    CacheQueryOptions 可选

  • 插件
  • copyRedirectedCacheableResponsesPlugin
  • defaultPrecacheCacheabilityPlugin
  • _awaitComplete

    void

    _awaitComplete 函数如下所示:

    (responseDone: Promise<Response>, handler: StrategyHandler, request: Request, event: ExtendableEvent) => {...}

    • responseDone

      Promise<Response>

    • handler
    • request

      请求

    • event

      ExtendableEvent

    • 返回

      Promise<void>

  • _getResponse

    void

    _getResponse 函数如下所示:

    (handler: StrategyHandler, request: Request, event: ExtendableEvent) => {...}

    • 返回

      Promise<Response>

  • _handleFetch

    void

    _handleFetch 函数如下所示:

    (request: Request, handler: StrategyHandler) => {...}

    • 返回

      Promise<Response>

  • _handleInstall

    void

    _handleInstall 函数如下所示:

    (request: Request, handler: StrategyHandler) => {...}

    • 返回

      Promise<Response>

  • 标识名

    void

    执行请求策略,并返回将通过 Response 进行解析的 Promise,从而调用所有相关插件回调。

    使用 Workbox workbox-routing.Route 注册策略实例时,系统会在路由匹配时自动调用此方法。

    或者,您可以将此方法传递给 event.respondWith(),从而在独立的 FetchEvent 监听器中使用该方法。

    handle 函数如下所示:

    (options: FetchEvent | HandlerCallbackOptions) => {...}

    • 返回

      Promise<Response>

  • handleAll

    void

    workbox-strategies.Strategy~handle 类似,但并非仅返回一个解析为 ResponsePromise,而是返回一个 [response, done] promise 的元组,其中前一个 (response) 等同于 handle() 返回的内容,而后一个是 promise,会在执行策略过程中添加到 event.waitUntil() 的任何 promise 完成后进行解析。

    您可以等待 done promise,以确保策略执行的任何额外工作(通常是缓存响应)成功完成。

    handleAll 函数如下所示:

    (options: FetchEvent | HandlerCallbackOptions) => {...}

    • 返回

      [Promise<Response>, Promise<void>]

      [response, complete] 元组 promise 可用于确定响应何时解析,以及处理程序何时完成所有工作。

urlManipulation()

workbox-precaching.urlManipulation(
  { url }: object,
)

类型

功能

参数

  • { 网址 }

    对象

    • 网址

      网址

返回

  • 网址 []

方法

addPlugins()

workbox-precaching.addPlugins(
  plugins: WorkboxPlugin[],
)

向预缓存策略添加插件。

参数

addRoute()

workbox-precaching.addRoute(
  options?: PrecacheRouteOptions,
)

向 Service Worker 添加 fetch 监听器,该监听器将使用预缓存的资源响应 [网络请求]https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests

对于未预缓存的资产的请求,FetchEvent 将不会得到响应,从而使事件能够传递给其他 fetch 事件监听器。

参数

cleanupOutdatedCaches()

workbox-precaching.cleanupOutdatedCaches()

添加了 activate 事件监听器,用于清理旧版 Workbox 创建的不兼容预缓存。

createHandlerBoundToURL()

workbox-precaching.createHandlerBoundToURL(
  url: string,
)

对默认 PrecacheController 实例调用 PrecacheController#createHandlerBoundToURL 的辅助函数。

如果您要创建自己的 PrecacheController,请在该实例上调用 PrecacheController#createHandlerBoundToURL,而不是使用此函数。

参数

  • 网址

    string

    将用于查找 Response 的预缓存网址。

getCacheKeyForURL()

workbox-precaching.getCacheKeyForURL(
  url: string,
)

接受一个网址,并返回对应的网址,该网址可用于查找预缓存中的条目。

如果提供了相对网址,则 Service Worker 文件的位置将用作基础。

对于没有修订版本信息的预缓存条目,缓存键将与原始网址相同。

对于包含修订版本信息的预缓存条目,缓存键将是原始网址,并添加了一个用于跟踪修订版本信息的查询参数。

参数

  • 网址

    string

    要获取其缓存键的网址。

返回

  • 字符串 | 未定义

    与该网址对应的缓存键。

matchPrecache()

workbox-precaching.matchPrecache(
  request: string | Request,
)

对默认 PrecacheController 实例调用 PrecacheController#matchPrecache 的辅助函数。

如果您要创建自己的 PrecacheController,请在该实例上调用 PrecacheController#matchPrecache,而不是使用此函数。

参数

  • request

    字符串 | 请求

    要在预缓存中查找的键(无需修改参数)。

返回

  • Promise<Response | undefined>

precache()

workbox-precaching.precache(
  entries: (string | PrecacheEntry)[],
)

在 Service Worker 安装时,将项添加到预缓存列表中,移除所有重复项并将文件存储在缓存中。

此方法可多次调用。

请注意:此方法不会为您传送任何缓存的文件。 它只能预缓存文件。如需响应网络请求,请调用 workbox-precaching.addRoute

如果您要预缓存单个文件数组,只需调用 workbox-precaching.precacheAndRoute 即可。

参数

precacheAndRoute()

workbox-precaching.precacheAndRoute(
  entries: (string | PrecacheEntry)[],
  options?: PrecacheRouteOptions,
)

此方法会将条目添加到预缓存列表中,并添加一个路由来响应提取事件。

这是一种在单次调用中调用 workbox-precaching.precacheworkbox-precaching.addRoute 的便捷方法。

参数