Service Worker 将 API 调用中的文件添加到预缓存
Service worker add files from API call to precache
启用我的应用 运行 离线。在安装过程中,service worker 应该:
- 从异步中获取 URL 列表 API
- 重新格式化响应
- 在对预缓存的响应中添加所有 URL
为此,我将 Googles Workbox 与 Webpack 结合使用。
问题:虽然 service worker 成功缓存了所有 Webpack 资产(这告诉我工作箱基本上做了它应该做的),但它没有等待异步 API 调用来缓存额外的远程资产。它们会被简单地忽略,既不会缓存也不会在网络中获取。
这是我的服务工作者代码:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');
workbox.skipWaiting();
workbox.clientsClaim();
self.addEventListener('install', (event) => {
const precacheController = new
workbox.precaching.PrecacheController();
const preInstallUrl = 'https://www.myapiurl/Assets';
event.waitUntil(fetch(preInstallUrl)
.then(response => response.json()
.then((Assets) => {
Object.keys(Assets.data.Assets).forEach((key) => {
precacheController.addToCacheList([Assets.data.Assets[key]]);
});
})
);
});
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');
这是我的工作箱 webpack 配置:
new InjectManifest({
swDest: 'sw.js',
swSrc: './src/sw.js',
globPatterns: ['dist/*.{js,png,html,css,gif,GIF,PNG,JPG,jpeg,woff,woff2,ttf,svg,eot}'],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
})
我意识到我的错误。我希望这也能帮助其他人。问题是我没有手动调用 precacheController.install()
。虽然此函数将自动执行,但它不会等待异步插入的其他预缓存文件。这就是为什么需要在所有预缓存发生后调用该函数的原因。这是工作代码:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');
workbox.skipWaiting();
workbox.clientsClaim();
const precacheController = new workbox.precaching.PrecacheController();
// Hook into install event
self.addEventListener('install', (event) => {
// Get API URL passed as query parameter to service worker
const preInstallUrl = new URL(location).searchParams.get('preInstallUrl');
// Fetch precaching URLs and attach them to the cache list
const assetsLoaded = fetch(preInstallUrl)
.then(response => response.json())
.then((values) => {
Object.keys(values.data.Assets).forEach((key) => {
precacheController.addToCacheList([values.data.Assets[key]]);
});
})
.then(() => {
// After all assets are added install them
precacheController.install();
});
event.waitUntil(assetsLoaded);
});
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');
看起来您正在创建自己的 PrecacheController
实例并使用 precacheAndRoute()
,它们实际上并不打算一起使用(在文档中没有很好地解释,它是仅在 this one place).
中提及
问题是 workbox.precaching.*
上的辅助方法实际上在幕后创建了它们自己的 PrecacheController
实例。由于您正在创建自己的 PrecacheController
实例并且 还 调用 workbox.precaching.precacheAndRoute([...])
,因此您最终会得到两个无法正常工作的 PrecacheController
实例在一起。
从您的代码示例来看,您似乎正在创建一个 PrecacheController
实例,因为您想要加载文件列表以在运行时进行预缓存。没关系,但是如果您要这样做,需要注意以下几点:
您的软件可能不会更新
Service Worker 更新通常在您调用 navigator.serviceWorker.register()
并且浏览器检测到 Service Worker 文件已更改时触发。这意味着如果您更改 /Assets
returns 但您的 service worker 文件的内容没有改变,您的 service worker 将不会更新。这就是为什么大多数人在他们的 service worker 中硬编码他们的预缓存列表(因为对这些文件的任何更改都会触发新的 service worker 安装)。
您必须手动添加自己的路线
我之前提到 workbox.precaching.precacheAndRoute([...])
在后台创建自己的 PrecacheController
实例。它还adds its own fetch
listener manually to respond to requests. That means if you're not using precacheAndRoute()
, you'll have to create your own router and define your own routes. Here are the docs on how to create routes: https://developers.google.com/web/tools/workbox/modules/workbox-routing.
启用我的应用 运行 离线。在安装过程中,service worker 应该:
- 从异步中获取 URL 列表 API
- 重新格式化响应
- 在对预缓存的响应中添加所有 URL
为此,我将 Googles Workbox 与 Webpack 结合使用。
问题:虽然 service worker 成功缓存了所有 Webpack 资产(这告诉我工作箱基本上做了它应该做的),但它没有等待异步 API 调用来缓存额外的远程资产。它们会被简单地忽略,既不会缓存也不会在网络中获取。
这是我的服务工作者代码:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');
workbox.skipWaiting();
workbox.clientsClaim();
self.addEventListener('install', (event) => {
const precacheController = new
workbox.precaching.PrecacheController();
const preInstallUrl = 'https://www.myapiurl/Assets';
event.waitUntil(fetch(preInstallUrl)
.then(response => response.json()
.then((Assets) => {
Object.keys(Assets.data.Assets).forEach((key) => {
precacheController.addToCacheList([Assets.data.Assets[key]]);
});
})
);
});
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');
这是我的工作箱 webpack 配置:
new InjectManifest({
swDest: 'sw.js',
swSrc: './src/sw.js',
globPatterns: ['dist/*.{js,png,html,css,gif,GIF,PNG,JPG,jpeg,woff,woff2,ttf,svg,eot}'],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
})
我意识到我的错误。我希望这也能帮助其他人。问题是我没有手动调用 precacheController.install()
。虽然此函数将自动执行,但它不会等待异步插入的其他预缓存文件。这就是为什么需要在所有预缓存发生后调用该函数的原因。这是工作代码:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.1.0/workbox-sw.js');
workbox.skipWaiting();
workbox.clientsClaim();
const precacheController = new workbox.precaching.PrecacheController();
// Hook into install event
self.addEventListener('install', (event) => {
// Get API URL passed as query parameter to service worker
const preInstallUrl = new URL(location).searchParams.get('preInstallUrl');
// Fetch precaching URLs and attach them to the cache list
const assetsLoaded = fetch(preInstallUrl)
.then(response => response.json())
.then((values) => {
Object.keys(values.data.Assets).forEach((key) => {
precacheController.addToCacheList([values.data.Assets[key]]);
});
})
.then(() => {
// After all assets are added install them
precacheController.install();
});
event.waitUntil(assetsLoaded);
});
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/^.*\.(jpg|JPG|gif|GIF|png|PNG|eot|woff(2)?|ttf|svg)$/, workbox.strategies.cacheFirst({ cacheName: 'image-cache', plugins: [new workbox.cacheableResponse.Plugin({ statuses: [0, 200] }), new workbox.expiration.Plugin({ maxEntries: 600 })] }), 'GET');
看起来您正在创建自己的 PrecacheController
实例并使用 precacheAndRoute()
,它们实际上并不打算一起使用(在文档中没有很好地解释,它是仅在 this one place).
问题是 workbox.precaching.*
上的辅助方法实际上在幕后创建了它们自己的 PrecacheController
实例。由于您正在创建自己的 PrecacheController
实例并且 还 调用 workbox.precaching.precacheAndRoute([...])
,因此您最终会得到两个无法正常工作的 PrecacheController
实例在一起。
从您的代码示例来看,您似乎正在创建一个 PrecacheController
实例,因为您想要加载文件列表以在运行时进行预缓存。没关系,但是如果您要这样做,需要注意以下几点:
您的软件可能不会更新 Service Worker 更新通常在您调用
navigator.serviceWorker.register()
并且浏览器检测到 Service Worker 文件已更改时触发。这意味着如果您更改/Assets
returns 但您的 service worker 文件的内容没有改变,您的 service worker 将不会更新。这就是为什么大多数人在他们的 service worker 中硬编码他们的预缓存列表(因为对这些文件的任何更改都会触发新的 service worker 安装)。您必须手动添加自己的路线 我之前提到
workbox.precaching.precacheAndRoute([...])
在后台创建自己的PrecacheController
实例。它还adds its ownfetch
listener manually to respond to requests. That means if you're not usingprecacheAndRoute()
, you'll have to create your own router and define your own routes. Here are the docs on how to create routes: https://developers.google.com/web/tools/workbox/modules/workbox-routing.