Service Worker 背景腳本實戰
在 Manifest V2 時代,擴充套件有一個名為 background.html 或 background.js 的東西,它是一個隱形的網頁,只要你的擴充套件被啟用,它就會一直存在於背景消耗記憶體。這導致如果使用者裝了幾十個套件,瀏覽器就會卡到爆炸。
在 Manifest V3 中,Google 強制把常駐的 Background Pages 砍掉,換成了 Service Workers。
1. Service Worker 是什麼?
你可以把 Service Worker 想像成一個「隨叫隨到、用完即丟」的打工仔。
- 當有事件發生時(例如使用者點擊圖示、或是定時器響起),瀏覽器會「喚醒」Service Worker 來執行任務。
- 任務執行完畢,或閒置超過幾秒鐘後,瀏覽器就會把它「殺掉 (Terminated)」以釋放記憶體。
[!CAUTION] 最大雷區:全域變數會消失! 因為 Service Worker 會被隨機關閉和重啟,你絕對不能在裡面依賴全域變數來儲存重要狀態。
// ❌ 絕對不要這樣做!變數 count 在 worker 被殺掉後就會歸零。 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: 套件剛裝好時執行(適合做初始化資料庫、顯示 onboarding 網頁)。chrome.tabs.onUpdated: 當使用者切換分頁或網頁載入完成時觸發(適合用來判斷是否要在特定網址啟動功能)。chrome.action.onClicked: 當使用者點擊右上角的套件圖示時觸發(注意:如果你的 manifest 有設定default_popup,這個事件將不會觸發,因為點擊行為已經被 popup 攔截了)。
5. Fetch API 與非同步處理
Service Worker 也是你與後端資料庫(如 Supabase)或外部 API 通訊的最佳場所。因為它不會被跨網域 (CORS) 限制得太死(只要你在 manifest 中有宣告)。
// 在 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,教你如何直接竄改使用者正在看的網頁畫面!