工作区预缓存

服务工作线程的一项功能是,能够在服务工作线程安装时将一组文件保存到缓存中。这通常称为“预缓存”,因为您是在使用服务工件之前缓存内容。

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

Workbox 通过简化 API 并确保高效下载资源,为您减轻了预缓存的许多繁重工作。

workbox-precaching 的工作原理

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

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

当用户稍后再次访问您的 Web 应用,而您有包含不同预缓存资源的新服务工件时,workbox-precaching 会查看新列表,并根据修订版本确定哪些资源是全新的,哪些现有资源需要更新。在新的服务工件的 install 事件期间,所有新的资源或更新后的修订版本都会添加到缓存中。

在其 activate 事件触发之前,系统不会使用以下新的服务工件来响应请求。在 activate 事件中,workbox-precaching 会检查当前网址的列表中是否不再存在任何缓存的素材资源,并将其从缓存中移除。

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

传送预缓存的响应

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

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

调用 precacheAndRoute()addRoute() 的顺序很重要。通常,您需要在服务工作器文件中尽早调用该方法,然后再向 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:这是一个 Node 软件包,可在 gulp 任务中使用或作为 npm 运行脚本。
  • workbox-webpack-plugin:webpack 用户可以使用此插件。
  • workbox-cli:我们的 CLI 还可用于生成资源列表并将其添加到您的服务工件。

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

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

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

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

忽略网址参数

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

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

您可以使用 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 监听器。对于熟悉服务工作器的开发者,如果您需要更多控制权,这可能不适合。

您可以直接使用 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)。

使用 Subresource Integrity

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

您可以将名为 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

    删除当前预缓存清单中不再存在的资源。从服务工件激活事件调用此方法。

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

    activate 函数如下所示:

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

    • 事件

      ExtendableEvent

  • addToCacheList

    void

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

    addToCacheList 函数如下所示:

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

    • entries

      (string | PrecacheEntry)[]

      要预缓存的条目数组。

  • createHandlerBoundToURL

    void

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

    createHandlerBoundToURL 函数如下所示:

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

    • 网址

      字符串

      预缓存的网址,用于查找 Response

  • getCacheKeyForURL

    void

    返回用于存储给定网址的缓存键。如果该网址没有版本号(例如“/index.html”),则缓存键将是原始网址,其中附加了搜索参数。

    getCacheKeyForURL 函数如下所示:

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

    • 网址

      字符串

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

    • 返回

      字符串

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

  • getCachedURLs

    void

    返回当前服务工件已预缓存的所有网址的列表。

    getCachedURLs 函数如下所示:

    () => {...}

    • 返回

      字符串[]

      预缓存的网址。

  • getIntegrityForCacheKey

    void

    getIntegrityForCacheKey 函数如下所示:

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

    • cacheKey

      字符串

    • 返回

      字符串

      与缓存键关联的子资源完整性,如果未设置,则返回 undefined。

  • getURLsToCacheKeys

    void

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

    getURLsToCacheKeys 函数如下所示:

    () => {...}

    • 返回

      Map<stringstring>

      指向缓存键映射的网址。

  • 安装

    void

    预缓存新的和更新的素材资源。从服务工件安装事件调用此方法。

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

    install 函数如下所示:

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

    • 事件

      ExtendableEvent

  • matchPrecache

    void

    它可作为 cache.match() 的直接替代项,但存在以下差异:

    • 它知道预缓存的名称,并且只会在该缓存中进行检查。
    • 借助此方法,您可以传入不含版本参数的“原始”网址,系统会自动为该网址的当前有效修订版本查找正确的缓存键。

    例如,matchPrecache('index.html') 将为当前活跃的服务工作器找到正确的预缓存响应,即使实际缓存键为 '/index.html?__WB_REVISION__=1234abcd' 也是如此。

    matchPrecache 函数如下所示:

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

    • request

      字符串 | 请求

      要在预缓存中查找的键(不带修订参数)。

    • 返回

      Promise<Response>

  • 预缓存

    void

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

    此方法可被多次调用。

    precache 函数如下所示:

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

PrecacheEntry

属性

  • 完整性

    字符串(选填)

  • 修订版本

    字符串(选填)

  • 网址

    字符串

PrecacheFallbackPlugin

借助 PrecacheFallbackPlugin,您可以指定在给定策略无法生成响应时要使用的“离线回退”响应。

它通过拦截 handlerDidError 插件回调并返回预缓存的响应来实现此目的,同时自动考虑预期的修订版本参数。

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

属性

  • 构造函数

    void

    使用关联的 fallback网址 构建新的 PrecacheFallbackPlugin。

    constructor 函数如下所示:

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

    • config

      对象

      • fallbackURL

        字符串

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

      • precacheController

        PrecacheController(可选)

PrecacheRoute

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

属性

PrecacheRouteOptions

属性

  • cleanURLs

    布尔值(可选)

  • directoryIndex

    字符串(选填)

  • ignoreURLParametersMatching

    RegExp[] 可选

  • urlManipulation

    urlManipulation(可选)

PrecacheStrategy

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

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

属性

  • 构造函数

    void

    constructor 函数如下所示:

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

    • 选项

      PrecacheStrategyOptions(可选)

  • cacheName

    字符串

  • fetchOptions

    RequestInit(可选)

  • matchOptions

    CacheQueryOptions(可选)

  • 插件
  • copyRedirectedCacheableResponsesPlugin
  • defaultPrecacheCacheabilityPlugin
  • _awaitComplete

    void

    _awaitComplete 函数如下所示:

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

    • responseDone

      Promise<Response>

    • handler
    • request

      请求

    • 事件

      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

    执行请求策略并返回一个 Promise,该 Promise 将解析为 Response,并调用所有相关的插件回调。

    将策略实例注册到 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, done] 两个 Promise 的元组,可用于确定响应何时解析以及处理程序何时完成所有工作。

urlManipulation()

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

类型

函数

参数

  • { url }

    对象

    • 网址

      网址

返回

  • 网址[]

方法

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,而不是使用此函数。

参数

  • 网址

    字符串

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

getCacheKeyForURL()

workbox-precaching.getCacheKeyForURL(
  url: 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

参数