Реалізація Background Service Worker у браузерному розширенні
У Manifest V3 фоновий скрипт став service worker. Це не просто перейменування — змінилась модель життєвого циклу. Service worker може бути завершений браузером в будь-який момент, коли немає активних задач, і це ламає паттерни, які працювали у MV2 з persistent background page.
Реєстрація у маніфесті
{
"manifest_version": 3,
"background": {
"service_worker": "background/sw.js",
"type": "module"
}
}
type: "module" дозволяє використовувати ES-модулі та import всередині service worker. Підтримується у Chrome 93+.
Життєвий цикл: що реально відбувається
Браузер запускає service worker при:
- встановленні/оновленні розширення
- отриманні повідомлення з content script або popup
- спрацьовуванні alarm (chrome.alarms)
- наступленні мережевої події (якщо підписаний)
Після завершення всіх обробників браузер може убити процес після ~30 секунд неактивності. Наступна подія поднімаме його знову — вже з чистим станом.
Це означає: ніякого глобального стану у пам'яті. Змінні не переживають перезапуск.
// ПОГАНО — стан втрачається при перезапуску SW
let requestCount = 0;
chrome.runtime.onMessage.addListener(() => {
requestCount++; // повернеться до 0 після перезапуску
});
// ДОБРЕ — зберігаємо у chrome.storage
chrome.runtime.onMessage.addListener(async () => {
const { requestCount = 0 } = await chrome.storage.local.get('requestCount');
await chrome.storage.local.set({ requestCount: requestCount + 1 });
});
Обробка подій: синхронна реєстрація обов'язкова
Обробники подій мають бути зареєстровані синхронно на верхньому рівні. Якщо реєструєте їх всередині async-функції або після await — браузер може не «побачити» їх при запуску SW для обробки події:
// background/sw.js
// ПРАВИЛЬНО — синхронна реєстрація на верхньому рівні
chrome.runtime.onInstalled.addListener(onInstalled);
chrome.runtime.onMessage.addListener(onMessage);
chrome.alarms.onAlarm.addListener(onAlarm);
// Реалізації можуть бути async
async function onInstalled(details) {
if (details.reason === 'install') {
await chrome.storage.sync.set({ settings: defaultSettings });
}
}
function onMessage(message, sender, sendResponse) {
handleMessage(message, sender).then(sendResponse);
return true; // мусить повернути true для async-ответів
}
Довгоживучі з'єднання через Port
Для задач, які займають більше кількох секунд (streaming, polling), використовуйте chrome.runtime.connect(). Активне з'єднання утримує SW живим:
chrome.runtime.onConnect.addListener((port) => {
if (port.name === 'long-running-task') {
handleLongRunningTask(port);
}
});
Терміни
Прості обробники подій: 1–2 дні. Складна з alarms, синхронізацією, довгоживучими задачами: 3–5 днів.







