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: Navigator
navigator) {
function addEventListener<"load">(type: "load", listener: (this: Window, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)
addEventListener('load', function () {
var navigator: Navigator
navigator.Navigator.serviceWorker: ServiceWorkerContainer
Available only in secure contexts.
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';
// Create a unique cache name for this deployment
const const CACHE: string
CACHE = `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 globalThis
self.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.
addEventListener('install', (event: Event
event) => {
// Create a new cache and add all files to it
async function function (local function) addFilesToCache(): Promise<void>
addFilesToCache() {
const const cache: Cache
cache = await var caches: CacheStorage
Available only in secure contexts.
caches.CacheStorage.open(cacheName: string): Promise<Cache>
open(const CACHE: string
CACHE);
await const cache: Cache
cache.Cache.addAll(requests: Iterable<RequestInfo>): Promise<void> (+1 overload)
addAll(const ASSETS: string[]
ASSETS);
}
event: Event
event.waitUntil(function (local function) addFilesToCache(): Promise<void>
addFilesToCache());
});
var self: Window & typeof globalThis
self.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.
addEventListener('activate', (event: Event
event) => {
// Remove previous cached data from disk
async function function (local function) deleteOldCaches(): Promise<void>
deleteOldCaches() {
for (const const key: string
key of await var caches: CacheStorage
Available only in secure contexts.
caches.CacheStorage.keys(): Promise<string[]>
keys()) {
if (const key: string
key !== const CACHE: string
CACHE) await var caches: CacheStorage
Available only in secure contexts.
caches.CacheStorage.delete(cacheName: string): Promise<boolean>
delete(const key: string
key);
}
}
event: Event
event.waitUntil(function (local function) deleteOldCaches(): Promise<void>
deleteOldCaches());
});
var self: Window & typeof globalThis
self.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.
addEventListener('fetch', (event: Event
event) => {
// ignore POST requests etc
if (event: Event
event.request.method !== 'GET') return;
async function function (local function) respond(): Promise<Response>
respond() {
const const url: URL
url = 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.
URL
class is a global reference for require('url').URL
https://node.dev.org.tw/api/url.html#the-whatwg-url-api
URL(event: Event
event.request.url);
const const cache: Cache
cache = await var caches: CacheStorage
Available only in secure contexts.
caches.CacheStorage.open(cacheName: string): Promise<Cache>
open(const CACHE: string
CACHE);
// `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.
includes(const url: URL
url.URL.pathname: string
pathname)) {
const const response: Response | undefined
response = await const cache: Cache
cache.Cache.match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise<Response | undefined>
match(const url: URL
url.URL.pathname: string
pathname);
if (const response: Response | undefined
response) {
return const response: Response
response;
}
}
// for everything else, try the network first, but
// fall back to the cache if we're offline
try {
const const response: Response
response = await function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)
fetch(event: Event
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 (!(const response: Response
response 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.
Response)) {
throw new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error('invalid response from fetch');
}
if (const response: Response
response.Response.status: number
status === 200) {
const cache: Cache
cache.Cache.put(request: RequestInfo | URL, response: Response): Promise<void>
put(event: Event
event.request, const response: Response
response.Response.clone(): Response
clone());
}
return const response: Response
response;
} catch (function (local var) err: unknown
err) {
const const response: Response | undefined
response = await const cache: Cache
cache.Cache.match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise<Response | undefined>
match(event: Event
event.request);
if (const response: Response | undefined
response) {
return const response: Response
response;
}
// 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: unknown
err;
}
}
event: Event
event.respondWith(function (local function) respond(): Promise<Response>
respond());
});
快取時請小心!在某些情況下,過時的資料可能比離線時無法使用的資料更糟糕。由於瀏覽器在快取太滿時會清空快取,因此您也應該小心快取大型資源,如影片檔案。
開發期間
service worker 會在生產環境中打包,但在開發期間不會。因此,只有支援service worker 中的模組的瀏覽器才能在開發時使用它們。如果您是手動註冊您的 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: Navigator
navigator.Navigator.serviceWorker: ServiceWorkerContainer
Available only in secure contexts.
serviceWorker.ServiceWorkerContainer.register(scriptURL: string | URL, options?: RegistrationOptions): Promise<ServiceWorkerRegistration>
register('/service-worker.js', {
RegistrationOptions.type?: WorkerType | undefined
type: 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 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;
這會停用對 DOM 類型(如 HTMLElement
)的存取,這些類型在 service worker 內不可用,並實例化正確的全域變數。將 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 文件。