Service Worker バックグラウンドスクリプト実践
Manifest V2 時代、拡張機能には background.html や background.js と呼ばれるものが存在し、これは拡張機能が有効な間常にメモリを消費する不可視のウェブページでした。これにより、ユーザーが数十の拡張機能をインストールすると、ブラウザが重くなる問題が発生していました。
Manifest V3 では、Google は常駐型の Background Pages を廃止し、Service Workers に置き換えました。
1. Service Worker とは?
Service Worker は「呼ばれたら働き、使い終わったら捨てられる」臨時労働者のようなものと考えられます。
- イベント発生時(ユーザーがアイコンをクリックしたり、タイマーが鳴った時など)、ブラウザは Service Worker を「起動」してタスクを実行させます。
- タスク完了後、または数秒間アイドル状態が続くと、ブラウザはメモリ解放のために Service Worker を「終了 (Terminated)」します。
[!CAUTION] 最大の落とし穴:グローバル変数が消える! Service Worker はランダムに終了・再起動されるため、重要な状態を保存するためにグローバル変数に依存してはいけません。
// ❌ 絶対にNG!workerが終了すると変数countはリセットされます let count = 0; chrome.action.onClicked.addListener(() => { count++; });正しい方法は
chrome.storageAPI を使って状態をディスクに保存することです。
2. Service Worker の登録
Service Worker を使用するには、まず manifest.json で登録する必要があります:
{
"manifest_version": 3,
"name": "My Service Worker App",
"version": "1.0",
"background": {
"service_worker": "background.js",
"type": "module"
},
"permissions": [
"storage",
"alarms"
]
}
type: "module" を追加している点に注意してください。これにより background.js で ES6 の import / export 構文が使えるようになり、大規模プロジェクトのコード管理が容易になります。
3. 実践:ポモドーロタイマーの作成 (Alarms API)
Service Worker では標準の setTimeout や setInterval が使えません(workerがスリープ状態になるとタイマーが停止するため)。代わりに Chrome 専用の chrome.alarms API を使用する必要があります。
background.js に簡単なポモドーロタイマーのロジックを実装してみましょう:
// 拡張機能のインストール/更新イベントを監視
chrome.runtime.onInstalled.addListener(() => {
console.log("拡張機能がインストールされました。初期設定を実行中...");
// デフォルトのポモドーロ時間を25分に設定
chrome.storage.local.set({ timerDuration: 25 });
});
// Popup や Content Script からのメッセージを監視
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'startTimer') {
// Alarmを作成
chrome.alarms.create('pomodoroTimer', { delayInMinutes: request.minutes });
console.log(`${request.minutes}分タイマーを設定しました!`);
sendResponse({ status: "success" });
}
});
// Alarm発報イベントを監視
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'pomodoroTimer') {
// 時間到了!通知を発行
console.log("時間です!休憩しましょう!");
// "notifications" 権限があればシステム通知を表示可能
chrome.notifications.create({
type: "basic",
iconUrl: "icons/icon128.png",
title: "ポモドーロ完了",
message: "お疲れ様でした!少し休憩しましょう!"
});
}
});
4. ライフサイクルとイベント駆動設計
V3 Service Worker 開発の核心は「イベント駆動 (Event-driven)」です。
background.js は chrome.xxx.addListener() でラップされたコードで構成されるべきです。
主なイベントリスナー:
chrome.runtime.onInstalled: 拡張機能インストール時(初期化やオンボーディングページ表示に最適)chrome.tabs.onUpdated: タブ切り替えやページ読み込み完了時(特定URLで機能を起動する判定に有用)chrome.action.onClicked: 拡張機能アイコンクリック時(注:default_popupが設定されている場合、このイベントは発生しません)
5. Fetch API と非同期処理
Service Worker はバックエンドデータベース(Supabaseなど)や外部APIとの通信にも最適です(manifestで宣言すればCORS制限が緩和されます)。
// background.js で外部APIを呼び出す
async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
const data = await response.json();
// 取得データをstorageに保存(popup.jsで読み取り可能)
await chrome.storage.local.set({ userData: data });
return data;
} catch (error) {
console.error("APIリクエスト失敗:", error);
}
}
まとめ
Service Worker は Manifest V3 の要です。そのランダムな終了特性に適応し、chrome.storage で状態を保存し、イベント駆動でプログラミングすることで、軽快でストレスのない拡張機能を開発できます。
次章では、拡張機能の「手足」となる Content Scripts を紹介し、ユーザーが閲覧中のウェブページを直接操作する方法を解説します!