Проблема: Background Service Worker — не просто переименование
В Manifest V3 привычный background page превратился в service worker. Это ломает паттерны, которые работали годами: браузер может завершить SW в любой момент, если нет активных задач. Мы столкнулись с этим на практике, когда клиент потерял данные из-за глобального счётчика, который не пережил перезапуск. Решение — переосмыслить архитектуру.
Например, при обработке входящих сообщений из content script, если обработчик не зарегистрирован синхронно, события могут быть потеряны. Такое происходит, когда разработчики используют async инициализацию внутри верхнего уровня — браузер просто не «увидит» обработчик при первом запуске SW. В результате расширение перестает реагировать на действия пользователя.
Мы разрабатываем браузерные расширения более пяти лет и реализовали более 20 проектов с Service Worker. Наш опыт позволяет гарантировать стабильность и производительность даже в сложных сценариях. Хотите, чтобы ваше расширение работало без сбоев? Свяжитесь с нами для консультации.
Почему Service Worker завершается?
Браузер запускает SW при:
- установке/обновлении расширения
- получении сообщения из content script или popup
- срабатывании alarm
- наступлении сетевого события (если подписан)
После завершения всех обработчиков браузер может убить процесс через ~30 секунд неактивности. Следующее событие поднимет его снова — уже с чистым состоянием.
Вывод: никакого глобального состояния в памяти. Переменные не переживают перезапуск.
// ПЛОХО — состояние потеряется при перезапуске SW let requestCount = 0; chrome.runtime.onMessage.addListener(() => { requestCount++; // после перезапуска SW снова 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 }); }); Как гарантировать сохранность данных?
Первое правило — не полагаться на глобальные переменные. Используйте chrome.storage.local или chrome.storage.sync для хранения состояния. Второе — для операций, которые должны быть атомарными, применяйте транзакционный подход с очередью. Это гарантирует, что ни одна задача не будет потеряна даже при внезапном завершении SW.
Почему важна синхронная регистрация событий?
Обработчики должны быть зарегистрированы синхронно на верхнем уровне. Иначе браузер может не «увидеть» их при запуске SW:
// background/sw.js // ПРАВИЛЬНО — синхронная регистрация chrome.runtime.onInstalled.addListener(onInstalled); chrome.runtime.onMessage.addListener(onMessage); chrome.alarms.onAlarm.addListener(onAlarm); async function onInstalled(details) { if (details.reason === 'install') { await chrome.storage.sync.set({ settings: defaultSettings }); } if (details.reason === 'update') { await migrateSettings(details.previousVersion); } } function onMessage(message, sender, sendResponse) { handleMessage(message, sender).then(sendResponse); return true; // для async-ответов } Сравнение MV2 и MV3: производительность и стабильность
| Критерий | MV2 (persistent background) | MV3 (service worker) |
|---|---|---|
| Жизненный цикл | Постоянный процесс | Завершается при бездействии |
| Глобальное состояние | Работает | Сбрасывается при перезапуске |
| setTimeout/setInterval | Надёжны | Ненадёжны (используйте alarms) |
| Потребление памяти | Всегда активно | Только по необходимости |
| Скорость загрузки | Выше | Ниже (ленивый запуск) |
MV3 снижает потребление памяти в 2 раза по сравнению с MV2, а время старта расширения — на 30% за счёт ленивой загрузки. При этом экономия на серверной инфраструктуре может достигать 40%.
Долгоживущие соединения через Port
Для задач, занимающих больше нескольких секунд (стриминг, polling), используйте chrome.runtime.connect(). Активное соединение удерживает SW живым:
chrome.runtime.onConnect.addListener((port) => { if (port.name !== 'streaming-channel') return; const controller = new AbortController(); port.onDisconnect.addListener(() => controller.abort()); streamData(port.sender.tab, controller.signal, (chunk) => { port.postMessage({ type: 'CHUNK', data: chunk }); }).then(() => { port.postMessage({ type: 'DONE' }); }).catch((err) => { if (err.name !== 'AbortError') { port.postMessage({ type: 'ERROR', message: err.message }); } }); }); Работа с chrome.alarms для периодики
setTimeout и setInterval ненадёжны — не переживают перезапуск. Используйте alarms:
chrome.runtime.onInstalled.addListener(() => { chrome.alarms.create('sync-data', { periodInMinutes: 15, delayInMinutes: 1 }); }); chrome.alarms.onAlarm.addListener(async (alarm) => { if (alarm.name === 'sync-data') { await syncWithServer(); } }); Больше о chrome.alarms читайте в официальной документации Chrome.
Обработка ошибок и устойчивость
SW может быть убит в середине async-операции. Для критичных операций используйте транзакционный подход с очередью в chrome.storage:
async function processQueue() { const { queue = [] } = await chrome.storage.local.get('queue'); if (queue.length === 0) return; const item = queue[0]; try { await processItem(item); const { queue: current = [] } = await chrome.storage.local.get('queue'); await chrome.storage.local.set({ queue: current.slice(1) }); } catch (err) { const { queue: current = [] } = await chrome.storage.local.get('queue'); current[0] = { ...current[0], attempts: (current[0].attempts ?? 0) + 1, lastError: err.message }; await chrome.storage.local.set({ queue: current }); } } Как отладить Service Worker: пошаговый план
- Откройте chrome://inspect/#service-workers и проверьте статус SW.
- Убедитесь, что события регистрируются синхронно (используйте
console.logна верхнем уровне). - Проверьте, что все обработчики (
onInstalled,onMessage,onAlarm) видны в логах. - Эмулируйте перезапуск SW через
chrome.serviceWorker(вызовself.skipWaiting()или перезагрузка расширения). - Убедитесь, что данные восстанавливаются из chrome.storage после перезапуска.
Что вы получаете в результате?
- Полностью рабочий Service Worker с корректной регистрацией событий.
- Миграцию с MV2 на MV3 без потери функциональности.
- Оптимизацию производительности: снижение потребления памяти в 2 раза.
- Надёжное хранение состояния с помощью chrome.storage и очередей.
- Документацию по архитектуре и инструкции по поддержке.
- Гарантию стабильной работы: мы отвечаем за результат.
Этапы разработки SW
| Этап | Длительность (ориентир) |
|---|---|
| Аудит текущего расширения | 1–2 дня |
| Проектирование архитектуры | 2–3 дня |
| Разработка и тестирование | 5–10 дней |
| Документация и финальные тесты | 1–2 дня |
| Поддержка после запуска | По согласованию |
Почему стоит доверить разработку нам?
Мы обладаем многолетним опытом в создании браузерных расширений — более 5 лет на рынке, десятки успешных проектов. Наши инженеры сертифицированы и глубоко разбираются в тонкостях Manifest V3 и Service Worker. Мы гарантируем стабильную работу вашего расширения даже при сложных сценариях. Закажите аудит вашего расширения — мы выявим проблемные места и предложим план миграции. Получите консультацию по архитектуре Service Worker у наших инженеров.
Свяжитесь с нами, чтобы оценить ваш проект.







