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
需要具有 url
和 revision
属性的对象数组。此数组有时称为预缓存清单:
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
会为您设置 install
和 activate
监听器。对于熟悉 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
-
返回
Promise<CleanupResult>
-
-
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
-
返回
Promise<InstallResult>
-
-
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)[]) => {...}
-
entries
(字符串 | PrecacheEntry)[]
-
PrecacheEntry
属性
-
完整性
字符串(可选)
-
修订版本
字符串(可选)
-
网址
string
PrecacheFallbackPlugin
PrecacheFallbackPlugin
允许您指定“离线回退”响应,以便在指定策略无法生成响应时使用。
为此,它会拦截 handlerDidError
插件回调并返回预缓存的响应,并自动考虑预期的修订版本参数。
除非您明确将 PrecacheController
实例传递给构造函数,否则系统将使用默认实例。一般来说,大多数开发者最终都会使用默认值。
属性
-
构造函数
void
使用关联的 fallback网址 构造一个新的 PrecacheFallbackPlugin。
constructor
函数如下所示:(config: object) => {...}
-
config
对象
-
fallbackURL
string
预缓存的网址,在关联的策略无法生成响应时用作后备网址。
-
precacheController
-
-
PrecacheRoute
workbox-routing.Route
的子类,接受 workbox-precaching.PrecacheController
实例,并使用该实例匹配传入请求并处理从预缓存中提取响应。
属性
-
构造函数
void
constructor
函数如下所示:(precacheController: PrecacheController, options?: PrecacheRouteOptions) => {...}
-
precacheController
一个
PrecacheController
实例,用于匹配请求和响应提取事件。 -
选项
-
-
catchHandler
-
handler
-
method
HTTPMethod
-
setCatchHandler
void
setCatchHandler
函数如下所示:(handler: RouteHandler) => {...}
-
handler
一个回调函数,用于返回一个解析为响应的 Promise
-
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) => {...}
-
handler
-
request
请求
-
event
ExtendableEvent
-
返回
Promise<Response>
-
-
_handleFetch
void
_handleFetch
函数如下所示:(request: Request, handler: StrategyHandler) => {...}
-
request
请求
-
handler
-
返回
Promise<Response>
-
-
_handleInstall
void
_handleInstall
函数如下所示:(request: Request, handler: StrategyHandler) => {...}
-
request
请求
-
handler
-
返回
Promise<Response>
-
-
标识名
void
执行请求策略,并返回将通过
Response
进行解析的Promise
,从而调用所有相关插件回调。使用 Workbox
workbox-routing.Route
注册策略实例时,系统会在路由匹配时自动调用此方法。或者,您可以将此方法传递给
event.respondWith()
,从而在独立的FetchEvent
监听器中使用该方法。handle
函数如下所示:(options: FetchEvent | HandlerCallbackOptions) => {...}
-
选项
FetchEvent | HandlerCallbackOptions
FetchEvent
或具有下列属性的对象。
-
返回
Promise<Response>
-
-
handleAll
void
与
workbox-strategies.Strategy~handle
类似,但并非仅返回一个解析为Response
的Promise
,而是返回一个[response, done]
promise 的元组,其中前一个 (response
) 等同于handle()
返回的内容,而后一个是 promise,会在执行策略过程中添加到event.waitUntil()
的任何 promise 完成后进行解析。您可以等待
done
promise,以确保策略执行的任何额外工作(通常是缓存响应)成功完成。handleAll
函数如下所示:(options: FetchEvent | HandlerCallbackOptions) => {...}
-
选项
FetchEvent | HandlerCallbackOptions
FetchEvent
或具有下列属性的对象。
-
返回
[Promise<Response>, Promise<void>]
[response, complete] 元组 promise 可用于确定响应何时解析,以及处理程序何时完成所有工作。
-
urlManipulation()
workbox-precaching.urlManipulation(
{ url }: object,
)
类型
功能
参数
-
{ 网址 }
对象
-
网址
网址
-
返回
-
网址 []
方法
参数
-
插件
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
即可。
参数
-
entries
(字符串 | PrecacheEntry)[]
precacheAndRoute()
workbox-precaching.precacheAndRoute(
entries: (string | PrecacheEntry)[],
options?: PrecacheRouteOptions,
)
此方法会将条目添加到预缓存列表中,并添加一个路由来响应提取事件。
这是一种在单次调用中调用 workbox-precaching.precache
和 workbox-precaching.addRoute
的便捷方法。
参数
-
entries
(字符串 | PrecacheEntry)[]
要预缓存的条目数组。
-
选项