Skip to main content

Service workers

Service workers 作为代理服务端,处理应用程序内部的网络请求。这使得您的应用程序能够离线工作,但即使您不需要离线支持(或由于您正在构建的应用程序类型而无法实现它),使用 service workers 预缓存构建的 JS 和 CSS 来加快导航速度,通常也是值得的。

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

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

if ('serviceWorker' in var navigator: Navigatornavigator) {
	function addEventListener<"load">(type: "load", listener: (this: Window, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)addEventListener('load', function () {
		var navigator: Navigatornavigator.Navigator.serviceWorker: ServiceWorkerContainer

Available only in secure contexts.

MDN Reference

serviceWorker
.ServiceWorkerContainer.register(scriptURL: string | URL, options?: RegistrationOptions): Promise<ServiceWorkerRegistration>register('./path/to/service-worker.js');
}); }

Service Worker 内部

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

以下示例会尽可能早的缓存构建的应用程序和 static 中的所有文件,并在访问时缓存所有其他请求。这将使每个页面在访问后都能离线工作。

/// <reference types="@sveltejs/kit" />
import { const build: string[]

An array of URL strings representing the files generated by Vite, suitable for caching with cache.addAll(build). During development, this is an empty array.

build
, const files: string[]

An array of URL strings representing the files in your static directory, or whatever directory is specified by config.kit.files.assets. You can customize which files are included from static directory using config.kit.serviceWorker.files

files
, const version: string

See config.kit.version. It’s useful for generating unique cache names inside your service worker, so that a later deployment of your app can invalidate old caches.

version
} from '$service-worker';
// 为此部署创建唯一的缓存名称 const const CACHE: stringCACHE = `cache-${const version: string

See config.kit.version. It’s useful for generating unique cache names inside your service worker, so that a later deployment of your app can invalidate old caches.

version
}`;
const const ASSETS: string[]ASSETS = [ ...const build: string[]

An array of URL strings representing the files generated by Vite, suitable for caching with cache.addAll(build). During development, this is an empty array.

build
, // 应用程序本身
...const files: string[]

An array of URL strings representing the files in your static directory, or whatever directory is specified by config.kit.files.assets. You can customize which files are included from static directory using config.kit.serviceWorker.files

files
// `static` 中的所有内容
]; var self: Window & typeof globalThisself.function addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void (+1 overload)

Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options’s capture.

When set to true, options’s capture prevents callback from being invoked when the event’s eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event’s eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event’s eventPhase attribute value is AT_TARGET.

When set to true, options’s passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

When set to true, options’s once indicates that the callback will only be invoked once after which the event listener will be removed.

If an AbortSignal is passed for options’s signal, then the event listener will be removed when signal is aborted.

The event listener is appended to target’s event listener list and is not appended if it has the same type, callback, and capture.

MDN Reference

addEventListener
('install', (event: Eventevent) => {
// 创建新缓存并添加所有文件 async function function (local function) addFilesToCache(): Promise<void>addFilesToCache() { const const cache: Cachecache = await var caches: CacheStorage

Available only in secure contexts.

MDN Reference

caches
.CacheStorage.open(cacheName: string): Promise<Cache>open(const CACHE: stringCACHE);
await const cache: Cachecache.Cache.addAll(requests: Iterable<RequestInfo>): Promise<void> (+1 overload)addAll(const ASSETS: string[]ASSETS); } event: Eventevent.waitUntil(function (local function) addFilesToCache(): Promise<void>addFilesToCache()); }); var self: Window & typeof globalThisself.function addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void (+1 overload)

Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options’s capture.

When set to true, options’s capture prevents callback from being invoked when the event’s eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event’s eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event’s eventPhase attribute value is AT_TARGET.

When set to true, options’s passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

When set to true, options’s once indicates that the callback will only be invoked once after which the event listener will be removed.

If an AbortSignal is passed for options’s signal, then the event listener will be removed when signal is aborted.

The event listener is appended to target’s event listener list and is not appended if it has the same type, callback, and capture.

MDN Reference

addEventListener
('activate', (event: Eventevent) => {
// 从磁盘删除以前的缓存数据 async function function (local function) deleteOldCaches(): Promise<void>deleteOldCaches() { for (const const key: stringkey of await var caches: CacheStorage

Available only in secure contexts.

MDN Reference

caches
.CacheStorage.keys(): Promise<string[]>keys()) {
if (const key: stringkey !== const CACHE: stringCACHE) await var caches: CacheStorage

Available only in secure contexts.

MDN Reference

caches
.CacheStorage.delete(cacheName: string): Promise<boolean>delete(const key: stringkey);
} } event: Eventevent.waitUntil(function (local function) deleteOldCaches(): Promise<void>deleteOldCaches()); }); var self: Window & typeof globalThisself.function addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void (+1 overload)

Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.

The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options’s capture.

When set to true, options’s capture prevents callback from being invoked when the event’s eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event’s eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event’s eventPhase attribute value is AT_TARGET.

When set to true, options’s passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners.

When set to true, options’s once indicates that the callback will only be invoked once after which the event listener will be removed.

If an AbortSignal is passed for options’s signal, then the event listener will be removed when signal is aborted.

The event listener is appended to target’s event listener list and is not appended if it has the same type, callback, and capture.

MDN Reference

addEventListener
('fetch', (event: Eventevent) => {
// 忽略 POST 请求等 if (event: Eventevent.request.method !== 'GET') return; async function function (local function) respond(): Promise<Response>respond() { const const url: URLurl = new var URL: new (url: string | URL, base?: string | URL) => URL

The URL interface represents an object providing static methods used for creating object URLs.

MDN Reference

URL class is a global reference for require('url').URL https://nodejs.org/api/url.html#the-whatwg-url-api

@sincev10.0.0
URL
(event: Eventevent.request.url);
const const cache: Cachecache = await var caches: CacheStorage

Available only in secure contexts.

MDN Reference

caches
.CacheStorage.open(cacheName: string): Promise<Cache>open(const CACHE: stringCACHE);
// `build`/`files` 始终可以从缓存中提供服务 if (const ASSETS: string[]ASSETS.Array<string>.includes(searchElement: string, fromIndex?: number): boolean

Determines whether an array includes a certain element, returning true or false as appropriate.

@paramsearchElement The element to search for.
@paramfromIndex The position in this array at which to begin searching for searchElement.
includes
(const url: URLurl.URL.pathname: stringpathname)) {
const const response: Response | undefinedresponse = await const cache: Cachecache.Cache.match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise<Response | undefined>match(const url: URLurl.URL.pathname: stringpathname); if (const response: Response | undefinedresponse) { return const response: Responseresponse; } } // 对于其他所有内容,首先尝试网络 // 但如果我们离线,则回退到缓存 try { const const response: Responseresponse = await function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)fetch(event: Eventevent.request); // 如果我们离线,fetch 可能返回非 Response 值 // 而不是抛出错误 - 我们不能将这个非 Response 传递给 respondWith if (!(const response: Responseresponse instanceof
var Response: {
    new (body?: BodyInit | null, init?: ResponseInit): Response;
    prototype: Response;
    error(): Response;
    json(data: any, init?: ResponseInit): Response;
    redirect(url: string | URL, status?: number): Response;
}

This Fetch API interface represents the response to a request.

MDN Reference

Response
)) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
('invalid response from fetch');
} if (const response: Responseresponse.Response.status: numberstatus === 200) { const cache: Cachecache.Cache.put(request: RequestInfo | URL, response: Response): Promise<void>put(event: Eventevent.request, const response: Responseresponse.Response.clone(): Responseclone()); } return const response: Responseresponse; } catch (function (local var) err: unknownerr) { const const response: Response | undefinedresponse = await const cache: Cachecache.Cache.match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise<Response | undefined>match(event: Eventevent.request); if (const response: Response | undefinedresponse) { return const response: Responseresponse; } // 如果没有缓存,就直接报错 // 因为我们无法对这个请求做任何响应 throw function (local var) err: unknownerr; } } event: Eventevent.respondWith(function (local function) respond(): Promise<Response>respond()); });

缓存时要小心!在某些情况下,过时的数据可能比离线时无法获取的数据更糟糕。由于浏览器会在缓存太满时清空缓存,因此您还应该谨慎缓存大型资源,如视频文件。

在开发过程中

service worker 在生产环境中会被打包,但在开发过程中不会。因此,只有支持 service workers 中的模块 的浏览器才能在开发时使用它们。如果您手动注册 service worker,在开发时需要传递 { type: 'module' } 选项:

import { const dev: boolean

Whether the dev server is running. This is not guaranteed to correspond to NODE_ENV or MODE.

dev
} from '$app/environment';
var navigator: Navigatornavigator.Navigator.serviceWorker: ServiceWorkerContainer

Available only in secure contexts.

MDN Reference

serviceWorker
.ServiceWorkerContainer.register(scriptURL: string | URL, options?: RegistrationOptions): Promise<ServiceWorkerRegistration>register('/service-worker.js', {
RegistrationOptions.type?: WorkerType | undefinedtype: const dev: boolean

Whether the dev server is running. This is not guaranteed to correspond to NODE_ENV or MODE.

dev
? 'module' : 'classic'
});

在开发环境中,buildprerendered 是空数组

类型安全

为 service workers 设置适当的类型需要一些手动设置。在您的 service-worker.js 中,在文件顶部添加以下内容:

/// <reference types="@sveltejs/kit" />
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />

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

const sw = self as unknown as ServiceWorkerGlobalScope;

这会禁用对 service worker 中不可用的 DOM 类型(如 HTMLElement)的访问,并实例化正确的全局变量。将 self 重新赋值给 sw 允许您在此过程中进行类型转换(有几种方法可以做到这一点,但这是最简单的,不需要额外的文件)。在文件的其余部分使用 sw 而不是 self

对 SvelteKit 类型的引用确保 $service-worker 导入具有适当的类型定义。如果您导入 $env/static/public,您要么必须使用 // @ts-ignore 注释导入,要么添加 /// <reference types="../.svelte-kit/ambient.d.ts" /> 到引用类型中。

其他解决方案

SvelteKit 的 service worker 实现故意保持低级别。如果您需要更全功能但也更有主见的解决方案,我们建议查看像 Vite PWA 插件 这样的解决方案,它使用 Workbox。有关 service workers 的更多一般信息,我们推荐 MDN web 文档

在 GitHub 编辑此页面

上一页 下一页