本文へスキップ

サービスワーカー

サービスワーカーは、アプリ内のネットワークリクエストを処理するプロキシサーバーとして機能します。これにより、アプリをオフラインで動作させることができますが、オフラインサポートが不要な場合(または構築しているアプリの種類のために現実的に実装できない場合でも)、構築済みのJSとCSSをプリキャッシュすることで、ナビゲーションを高速化するためにサービスワーカーを使用する価値はしばしばあります。

SvelteKitでは、`src/service-worker.js`ファイル(または`src/service-worker/index.js`)がある場合、それはバンドルされ、自動的に登録されます。必要に応じて、サービスワーカーの場所を変更できます。

独自のロジックでサービスワーカーを登録する必要がある場合、または別のソリューションを使用する必要がある場合は、自動登録を無効にできます。デフォルトの登録は次のようになります。

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`モジュールにアクセスできます。これは、すべての静的アセット、ビルドファイル、プリレンダリングされたページへのパスを提供します。また、一意のキャッシュ名を作成するために使用できるアプリのバージョン文字列と、デプロイの`base`パスも提供されます。Viteの設定で`define`を指定した場合(グローバル変数の置換に使用)、これはサービスワーカーとサーバー/クライアントのビルドの両方に適用されます。

次の例では、構築されたアプリと`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';
// Create a unique cache name for this deployment 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
, // the app itself
...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
// everything in `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) => {
// Create a new cache and add all files to it 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) => {
// Remove previous cached data from disk 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) => {
// ignore POST requests etc 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://node.dokyumento.jp/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` can always be served from the cache 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; } } // for everything else, try the network first, but // fall back to the cache if we're offline try { const const response: Responseresponse = await function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)fetch(event: Eventevent.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 (!(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; } // if there's no cache, then just error out // as there is nothing we can do to respond to this request throw function (local var) err: unknownerr; } } event: Eventevent.respondWith(function (local function) respond(): Promise<Response>respond()); });

キャッシュには注意してください!場合によっては、古いデータはオフライン時のデータがない状態よりも悪いかもしれません。ブラウザはキャッシュがいっぱいになるとキャッシュを空にするため、ビデオファイルのような大規模なアセットのキャッシュにも注意する必要があります。

開発中

サービスワーカーは本番用にバンドルされますが、開発中はバンドルされません。そのため、モジュールをサービスワーカーでサポートしているブラウザのみが、開発時にそれらを使用できます。サービスワーカーを手動で登録している場合は、開発時に`{ 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'
});

`build`と`prerendered`は、開発中は空の配列です。

型安全

サービスワーカーの適切な型を設定するには、いくつかの手動設定が必要です。`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;

これは、サービスワーカー内では使用できない`HTMLElement`などのDOM型へのアクセスを無効にし、正しいグローバル変数をインスタンス化します。`self`を`sw`に再代入することで、その過程で型キャストできます(これを行う方法はいくつかありますが、追加ファイルが不要なため、これが最も簡単です)。ファイルの残りの部分では、`self`の代わりに`sw`を使用します。SvelteKitの型への参照により、`$service-worker`のインポートに適切な型定義が確実に含まれます。`$env/static/public`をインポートする場合は、インポートを`// @ts-ignore`するか、参照型に`/// `を追加する必要があります。

その他のソリューション

SvelteKitのサービスワーカーの実装は、意図的に低レベルです。より本格的だが、より意見の強いソリューションが必要な場合は、Vite PWAプラグインWorkboxを使用)などのソリューションを検討することをお勧めします。サービスワーカーに関するより一般的な情報については、MDN Webドキュメントをお勧めします。

GitHubでこのページを編集