こんにちは。よっしーです(^^)
今日は、SvelteKitでのService workersについて解説しています。
背景
SvelteKitでのService workersについて調査する機会がありましたので、その時の内容を備忘として記事に残しました。
Service workers
Service workerはアプリ内のネットワークリクエストを処理するプロキシサーバーとして動作します。これによりアプリをオフラインで動作させることが可能になりますが、たとえオフラインサポートが不要な場合(または構築しているアプリの性質上、現実的に実装できない場合)でも、ビルドされたJSとCSSを事前にキャッシュすることでナビゲーションを高速化するためにService workerを使用する価値があります。
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の基本的な役割:
- ネットワークリクエストの仲介役として機能
- プロキシサーバーのように動作し、アプリケーションのネットワーク通信を制御
- 主な利点:
- オフラインサポート機能の実現
- ページナビゲーションの高速化
- JS/CSSファイルの事前キャッシュによるパフォーマンス向上
- SvelteKitでの特徴:
- 特定のファイルパスに配置するだけで自動的に設定される
- カスタマイズ可能な柔軟性を持つ
- 必要に応じて自動登録を無効化することも可能
- 登録処理の説明:
- ブラウザがService worker機能をサポートしているか確認
- ページ読み込み完了時にService workerを登録
- 指定されたパスのService workerファイルを使用
このような機能は、モダンなウェブアプリケーションにおいて、特にパフォーマンスとユーザー体験の向上に重要な役割を果たします。
Service Workerの実装
Service Worker内では、$service-worker
モジュールにアクセスできます。このモジュールは、すべての静的アセット、ビルドファイル、プリレンダリングされたページへのパスを提供します。また、ユニークなキャッシュ名の作成に使用できるアプリバージョン文字列と、デプロイメントのbase
パスも提供されます。Viteの設定でdefine
(グローバル変数の置換に使用)を指定している場合、これはサーバー/クライアントビルドと同様にService Workerにも適用されます。
次の例では、ビルドされたアプリケーションとstatic
内のすべてのファイルを積極的にキャッシュし、その他のリクエストも発生時にキャッシュします。これにより、一度訪問したページはオフラインでも動作するようになります。
解説
- 初期設定部分:
import { build, files, version } from '$service-worker';
const CACHE = `cache-${version}`;
const ASSETS = [
...build, // アプリケーション本体
...files // staticディレクトリの全ファイル
];
$service-worker
から必要なモジュールをインポート- バージョン管理されたキャッシュ名を生成
- キャッシュ対象のアセットを定義
- インストールイベントハンドラ:
self.addEventListener('install', (event) => {
async function addFilesToCache() {
const cache = await caches.open(CACHE);
await cache.addAll(ASSETS);
}
event.waitUntil(addFilesToCache());
});
- Service Worker初期インストール時に実行
- 新しいキャッシュストレージを開き、すべてのアセットを保存
- アクティベーションイベントハンドラ:
self.addEventListener('activate', (event) => {
async function deleteOldCaches() {
for (const key of await caches.keys()) {
if (key !== CACHE) await caches.delete(key);
}
}
event.waitUntil(deleteOldCaches());
});
- 古いバージョンのキャッシュを削除
- 新しいService Workerがアクティブになる際のクリーンアップ処理
- フェッチイベントハンドラ:
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
async function respond() {
const url = new URL(event.request.url);
const cache = await caches.open(CACHE);
// アセットのキャッシュチェック
if (ASSETS.includes(url.pathname)) {
const response = await cache.match(url.pathname);
if (response) return response;
}
// その他のリクエスト処理
try {
const response = await fetch(event.request);
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());
});
- GETリクエストのみを処理
- キャッシュ済みアセットの高速提供
- ネットワークファーストの戦略を採用
- オフライン時のフォールバック処理
【重要な注意点】
- キャッシュの使用には慎重な判断が必要
- 古いデータは、オフライン時のデータ不可用性よりも問題になる可能性がある
- ブラウザは容量制限に達するとキャッシュを自動的にクリア
- 動画などの大容量ファイルのキャッシュには特に注意が必要
このService Worker実装により、効率的なキャッシュ戦略とオフラインサポートを実現し、アプリケーションのパフォーマンスと信頼性を向上させることができます。
おわりに
今日は、 SvelteKitでのService workersについて解説しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント