跳至主要內容

進階

服務工作人員

在 GitHub 上編輯此頁面

服務工作人員扮演代理伺服器的角色,處理應用程式內的網路要求。這讓你的應用程式可以離線運作,但即使你不需要離線支援(或因為你正在建構的應用程式類型而無法實際實作),使用服務工作人員來透過預先快取已建置的 JS 和 CSS 來加速導覽通常是值得的。

在 SvelteKit 中,如果你有 src/service-worker.js 檔案(或 src/service-worker/index.js),它將會被綑綁並自動註冊。如果你需要的話,可以變更 服務工作人員的位置

如果你需要使用自己的邏輯註冊服務工作人員或使用其他解決方案,你可以 停用自動註冊。預設的註冊看起來像這樣

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

在服務工作人員內部

在服務工作人員內部,你可以存取 $service-worker 模組,它會提供所有靜態資源、建置檔案和預先渲染頁面的路徑。你還會獲得一個應用程式版本字串,你可以使用它來建立一個唯一的快取名稱,以及部署的 base 路徑。如果你的 Vite 設定指定 define(用於全域變數替換),這將會套用於服務工作人員以及你的伺服器/用戶端建置。

以下範例會積極快取已建置的應用程式和 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 網路文件

前一頁 連結選項