html - 如何将应用程序缓存清单转换为服务工作者
问题描述
应用程序缓存现已弃用, Chrome等浏览器正在移除支持。
我们有一个应用程序可以 100% 离线工作,同时将数据存储在 indexeddb 中,并稍后在用户重新在线时同步。我们需要将此站点从使用应用程序缓存转换为服务工作者。我们将为我们的服务人员使用Workbox。
我们必须隐藏缓存清单的三个主要部分。
CACHE 部分 这是要预缓存的资产列表。这可能是最直接的过渡,因为我们使用工作箱来预缓存这些文件。
NETWORK 部分 我们在这里使用 * (可能是最常见的),所以这可能不会成为问题。
FALLBACK 部分 我们在后备部分中有很多条目。基本上,它们被重定向到登录页面,以防有人离线刷新页面。
例子:
FALLBACK:
/search /login
/customer-edit /login
/foo-bar-baz /login
...
我的问题:
是否有 1) 将应用程序缓存/缓存清单转换为服务工作者的一般指南,或者 2) 将 FALLBACK 部分转换为服务工作者中的等效功能的一些具体指南。
Google 和 Duck Duck Go 的帮助不大。
现有项目将应用程序缓存升级到服务工作者,但大多数看起来都是测试版,例如来自 Google Chrome 实验室:github.com/GoogleChromeLabs/sw-appcache-behavior
解决方案
这是我使用Google 的 workbox提出的解决方案。
旁注:Workbox 似乎为大多数常见的 service worker 用例提供了解决方案,并且具有非常灵活的分发模型,无论是在 vanilla js 环境中还是在您选择的框架中,它都非常容易使用。
我们最终将我们的服务器端 AppCache(缓存清单代码)转换为生成服务工作者。(如何预缓存来自 json 文件的 url 列表?)
根据您的服务器端语言,您的代码会有所不同,但这是对我们有用的最终产品:
service-worker.js(生成的服务器端)
const productVersion = "3.01";
importScripts('/assets/workbox/workbox-sw.js');
workbox.setConfig({
modulePathPrefix: '/assets/workbox/'
});
const { precacheAndRoute, createHandlerBoundToURL } = workbox.precaching;
const { NavigationRoute, registerRoute, setCatchHandler } = workbox.routing;
precacheAndRoute([
// cache index html
{url: '/', revision: '3.01' },
// web workers
{url: '/assets/some-worker.js?ver=3.01', revision: '' },
{url: '/assets/other-worker.js?ver=3.01', revision: '' },
// other js files
{url: '/assets/shared-function.js', revision: '3.01' },
// ... removed for brevity
// css
{url: '/assets/site.css', revision: '3.01' },
{url: '/assets/fonts/fonts.css', revision: '3.01' },
// svg's
{url: '/assets/images/icon.svg', revision: '3.01' },
{url: '/assets/images/icon-2.svg', revision: '3.01' },
// png's
{url: '/assets/images/img-1.png', revision: '3.01' },
{url: '/assets/images/favicon/apple-touch-icon-114x114.png', revision: '3.01' },
// ...
// ...
// fonts
{url: '/assets/fonts/lato-bla-webfont.eot', revision: '3.01' },
{url: '/assets/fonts/lato-bla-webfont.ttf', revision: '3.01' },
// sounds
{url: '/assets/sounds/keypress.ogg', revision: '3.01' },
{url: '/assets/sounds/sale.ogg', revision: '3.01' },
]);
// Routing for SPA
// This assumes DEFAULT_URL has been precached.
const DEFAULT_URL = '/';
const handler = createHandlerBoundToURL(DEFAULT_URL);
const navigationRoute = new NavigationRoute(handler, {
denylist: [
new RegExp('/ping'),
new RegExp('/upgrade'),
new RegExp('/cache.manifest'),
],
});
registerRoute(navigationRoute);
// This allows the main window to signal the service worker that
// it should go ahead and install if it's waiting.
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
skipWaiting();
}
});
- 还有一些其他的事情需要注意。我们必须弄清楚如何从 App?Cache 平滑升级到 Service Worker。事实证明,为我们生成一个空的缓存清单起到了作用。
- 我们已经有一个升级过程(提示用户升级或使用倒计时强制自动升级),所以我们必须做一些工作才能让服务人员使用它。注意服务工作者文件的末尾有
addEventListener
代码。我们实际上是从升级页面调用它以获得平滑的升级过程。它是这样的:
A)升级脚本检测到新版本可用(有很多方法可以做到这一点,api调用轮询等)
B)如果用户接受或计时器到期,则将用户重定向到upgrade
页面。这一步至关重要,因为您无法在应用仍在运行的情况下更新服务人员。所以导航到升级页面,等待服务工作者安装,然后告诉它跳过等待并重定向到主(登录)屏幕。
C) 用户正在愉快地运行新版本的应用程序。
升级页面代码:(这是显示某种类型的“更新”用户界面的好页面)
<script type="module">
import { Workbox } from '/assets/workbox/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/serviceworker');
// This code exists b/c a service worker can't update with just a refresh/reload in the
// browser. This is b/c on a reload, the old and new page exist simultaneously and the old MUST
// unload before the new service worker can automatically assume control. Also if multiple pages
// are open, this blocks the service worker from taking control (multiple pages should not an issue with this app).
// This code activates a waiting service worker and _then_ redirects back to the app.
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
wb.addEventListener('waiting', (event) => {
// Set up a listener that will reload the page as soon as the previously waiting
// service worker has taken control.
wb.addEventListener('controlling', (event) => {
window.location.replace('/login');
});
// Send a message telling the service worker to skip waiting.
// This will trigger the `controlling` event handler above.
wb.messageSW({type: 'SKIP_WAITING' });
});
wb.register();
}
// set a timeout in case the service worker has already installed.
setTimeout(function () {
window.location.replace('/login');
}, 30000);
</script>
主页面(index.html 等)(如果用户来到应用程序并准备激活服务器工作者,因此需要刷新才能加载正确的资产/代码)
<script type="module">
import { Workbox } from '/assets/workbox/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/serviceworker');
wb.addEventListener('activated', (event) => {
// `event.isUpdate` will be true if another version of the service
// worker was controlling the page when this version was registered.
if (!event.isUpdate) {
// service worker was updated and activated for the first time.
// If your service worker is configured to precache assets, those
// assets should all be available now.
// this will only happen if the browser was closed when a new version was made available
// and it will only happen once per service worker install.
// Reload to so all libs are correct version.
window.location.reload(true);
}
});
wb.register();
}
</script>