跳至主要内容

高级

Service workers

在 GitHub 上编辑此页面

Service workers 充当代理服务器,处理应用内的网络请求。这使得你的应用可以在离线状态下运行,但即使你不需要离线支持(或由于你正在构建的应用类型而无法实际实现),通常也值得使用 service workers 通过预缓存已构建的 JS 和 CSS 来加快导航速度。

在 SvelteKit 中,如果你有一个 src/service-worker.js 文件(或 src/service-worker/index.js),它将被捆绑并自动注册。如果需要,你可以更改 service worker 的位置

如果你需要使用自己的逻辑注册 service worker 或使用其他解决方案,则可以 禁用自动注册。默认注册看起来像这样

ts
if ('serviceWorker' in navigator) {
addEventListener('load', function () {
navigator.serviceWorker.register('./path/to/service-worker.js');
});
}

在 service worker 内

在 service worker 内,你可以访问 $service-worker 模块,它为你提供所有静态资产、构建文件和预渲染页面的路径。你还可以获得一个应用版本字符串,可用于创建唯一的缓存名称,以及部署的 base 路径。如果你的 Vite 配置指定了 define(用于全局变量替换),它将应用于 service workers 以及你的服务器/客户端构建。

以下示例急切地缓存了构建的应用和 static 中的任何文件,并缓存所有其他请求,因为它们发生了。这将使每个页面在访问后离线运行。

ts
/// <reference types="@sveltejs/kit" />
import { build, files, version } from '$service-worker';
// Create a unique cache name for this deployment
const CACHE = `cache-${version}`;
const ASSETS = [
...build, // the app itself
...files // everything in `static`
];
self.addEventListener('install', (event) => {
// Create a new cache and add all files to it
async function addFilesToCache() {
const cache = await caches.open(CACHE);
Property 'waitUntil' does not exist on type 'Event'.2339Property 'waitUntil' does not exist on type 'Event'.
await cache.addAll(ASSETS);
}
event.waitUntil(addFilesToCache());
});
self.addEventListener('activate', (event) => {
// Remove previous cached data from disk
async function deleteOldCaches() {
for (const key of await caches.keys()) {
if (key !== CACHE) await caches.delete(key);
Property 'waitUntil' does not exist on type 'Event'.2339Property 'waitUntil' does not exist on type 'Event'.
}
}
event.waitUntil(deleteOldCaches());
});
Property 'request' does not exist on type 'Event'.2339Property 'request' does not exist on type 'Event'.
self.addEventListener('fetch', (event) => {
// ignore POST requests etc
Property 'request' does not exist on type 'Event'.2339Property 'request' does not exist on type 'Event'.
if (event.request.method !== 'GET') return;
async function respond() {
const url = new URL(event.request.url);
const cache = await caches.open(CACHE);
// `build`/`files` can always be served from the cache
if (ASSETS.includes(url.pathname)) {
const response = await cache.match(url.pathname);
if (response) {
return response;
}
}
Property 'request' does not exist on type 'Event'.2339Property 'request' does not exist on type 'Event'.
// for everything else, try the network first, but
// fall back to the cache if we're offline
try {
const response = await fetch(event.request);
// if we're offline, fetch can return a value that is not a Response
// instead of throwing - and we can't pass this non-Response to respondWith
if (!(response instanceof Response)) {
throw new Error('invalid response from fetch');
Property 'request' does not exist on type 'Event'.2339Property 'request' does not exist on type 'Event'.
}
if (response.status === 200) {
cache.put(event.request, response.clone());
}
Property 'request' does not exist on type 'Event'.2339Property 'request' does not exist on type 'Event'.
return response;
} catch (err) {
const response = await cache.match(event.request);
if (response) {
return response;
}
// if there's no cache, then just error out
// as there is nothing we can do to respond to this request
throw err;
Property 'respondWith' does not exist on type 'Event'.2339Property 'respondWith' does not exist on type 'Event'.
}
}
event.respondWith(respond());
});

缓存时要小心!在某些情况下,过时数据可能比离线时不可用的数据更糟糕。由于浏览器会在缓存过满时清空缓存,因此你还应小心缓存视频文件等大型资产。

在开发期间

服务工作者已打包用于生产,但未在开发期间打包。因此,只有支持 服务工作者中的模块 的浏览器才能在开发时使用它们。如果你手动注册你的服务工作者,则需要在开发中传递 { type: 'module' } 选项

ts
import { dev } from '$app/environment';
navigator.serviceWorker.register('/service-worker.js', {
type: dev ? 'module' : 'classic'
});

buildprerendered 在开发期间是空数组

类型安全性

为服务工作者设置适当的类型需要一些手动设置。在你的 service-worker.js 中,将以下内容添加到文件顶部

ts
/// <reference types="@sveltejs/kit" />
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />
const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self));
ts
/// <reference types="@sveltejs/kit" />
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />
const sw = self as unknown as ServiceWorkerGlobalScope;

这会禁用对 DOM 类型(如 HTMLElement)的访问,这些类型在服务工作者中不可用,并实例化正确的全局变量。将 self 重新分配给 sw 允许你在进程中对其进行类型转换(有几种方法可以做到这一点,但这是最简单的,不需要其他文件)。在文件的其余部分中使用 sw 而不是 self。对 SvelteKit 类型的引用确保 $service-worker 导入具有正确的类型定义。如果你导入 $env/static/public,则必须 // @ts-ignore 导入或将 /// <reference types="../.svelte-kit/ambient.d.ts" /> 添加到引用类型。

其他解决方案

SvelteKit 的服务工作者实现故意是低级别的。如果你需要一个更成熟但更有主见的解决方案,我们建议查看 Vite PWA 插件 等解决方案,它使用 Workbox。有关服务工作者的更多一般信息,我们推荐 MDN 网络文档

上一个 链接选项