Stories

Detail Return Return

SvelteKit 最新中文文檔教程(16)—— Service workers - Stories Detail

前言

Svelte,一個語法簡潔、入門容易,面向未來的前端框架。

從 Svelte 誕生之初,就備受開發者的喜愛,根據統計,從 2019 年到 2024 年,連續 6 年一直是開發者最感興趣的前端框架 No.1

image.png

Svelte 以其獨特的編譯時優化機制著稱,具有輕量級高性能易上手等特性,非常適合構建輕量級 Web 項目

為了幫助大家學習 Svelte,我同時搭建了 Svelte 最新的中文文檔站點。

如果需要進階學習,也可以入手我的小冊《Svelte 開發指南》,語法篇、實戰篇、原理篇三大篇章帶你係統掌握 Svelte!

歡迎圍觀我的“網頁版朋友圈”、加入“冴羽·成長陪伴社羣”,踏上“前端大佬成長之路”。

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 navigator) {
    addEventListener('load', function () {
        navigator.serviceWorker.register('./path/to/service-worker.js');
    });
}

Service Worker 內部

在 service worker 內部,您可以訪問 $service-worker 模塊,它為您提供所有靜態資源、構建文件和預渲染頁面的路徑。您還會獲得一個應用程序版本字符串,可用於創建唯一的緩存名稱,以及部署的 base 路徑。如果您的 Vite 配置指定了 define(用於全局變量替換),這也將應用於 service workers 以及服務端/客户端構建。

以下示例會盡可能早的緩存構建的應用程序和 static 中的所有文件,並在訪問時緩存所有其他請求。這將使每個頁面在訪問後都能離線工作。

// @errors: 2339
/// <reference types="@sveltejs/kit" />
import { build, files, version } from '$service-worker';

// 為此部署創建唯一的緩存名稱
const CACHE = `cache-${version}`;

const ASSETS = [
    ...build, // 應用程序本身
    ...files // `static` 中的所有內容
];

self.addEventListener('install', (event) => {
    // 創建新緩存並添加所有文件
    async function addFilesToCache() {
        const cache = await caches.open(CACHE);
        await cache.addAll(ASSETS);
    }

    event.waitUntil(addFilesToCache());
});

self.addEventListener('activate', (event) => {
    // 從磁盤刪除以前的緩存數據
    async function deleteOldCaches() {
        for (const key of await caches.keys()) {
            if (key !== CACHE) await caches.delete(key);
        }
    }

    event.waitUntil(deleteOldCaches());
});

self.addEventListener('fetch', (event) => {
    // 忽略 POST 請求等
    if (event.request.method !== 'GET') return;

    async function respond() {
        const url = new URL(event.request.url);
        const cache = await caches.open(CACHE);

        // `build`/`files` 始終可以從緩存中提供服務
        if (ASSETS.includes(url.pathname)) {
            const response = await cache.match(url.pathname);

            if (response) {
                return response;
            }
        }

        // 對於其他所有內容,首先嚐試網絡
        // 但如果我們離線,則回退到緩存
        try {
            const response = await fetch(event.request);

            // 如果我們離線,fetch 可能返回非 Response 值
            // 而不是拋出錯誤 - 我們不能將這個非 Response 傳遞給 respondWith
            if (!(response instanceof Response)) {
                throw new Error('invalid response from fetch');
            }

            if (response.status === 200) {
                cache.put(event.request, response.clone());
            }

            return response;
        } catch (err) {
            const response = await cache.match(event.request);

            if (response) {
                return response;
            }

            // 如果沒有緩存,就直接報錯
            // 因為我們無法對這個請求做任何響應
            throw err;
        }
    }

    event.respondWith(respond());
});
[!NOTE] 緩存時要小心!在某些情況下,過時的數據可能比離線時無法獲取的數據更糟糕。由於瀏覽器會在緩存太滿時清空緩存,因此您還應該謹慎緩存大型資源,如視頻文件。

在開發過程中

service worker 在生產環境中會被打包,但在開發過程中不會。因此,只有支持 service workers 中的模塊 的瀏覽器才能在開發時使用它們。如果您手動註冊 service worker,在開發時需要傳遞 { type: 'module' } 選項:

import { dev } from '$app/environment';

navigator.serviceWorker.register('/service-worker.js', {
    type: dev ? 'module' : 'classic'
});
[!NOTE] 在開發環境中,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 文檔。

Svelte 中文文檔

點擊查看中文文檔:SvelteKit Service workers

系統學習 Svelte,歡迎入手小冊《Svelte 開發指南》。語法篇、實戰篇、原理篇三大篇章帶你係統掌握 Svelte!

此外我還寫過 JavaScript 系列、TypeScript 系列、React 系列、Next.js 系列、冴羽答讀者問等 14 個系列文章, 全系列文章目錄:https://github.com/mqyqingfeng/Blog

歡迎圍觀我的“網頁版朋友圈”、加入“冴羽·成長陪伴社羣”,踏上“前端大佬成長之路”。

user avatar user_2dx56kla Avatar wenshushushushu Avatar columsys Avatar axui Avatar
Favorites 4 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.