פיתוח Background Service Worker להרחבות דפדפן
בעיה: Background Service Worker אינו רק שינוי שם
ב-Manifest V3, דף הרקע המוכר הפך ל-service worker. זה שובר תבניות שעבדו שנים: הדפדפן יכול להפסיק את ה-SW בכל רגע אם אין משימות פעילות. נתקלנו בזה בפועל כאשר לקוח איבד נתונים עקב מונה גלובלי שלא שרד אתחול מחדש. הפתרון הוא לחשוב מחדש על הארכיטקטורה.
לדוגמה, בעת עיבוד הודעות נכנסות מ-content script, אם ה-handler לא נרשם באופן סינכרוני, אירועים עלולים ללכת לאיבוד. זה קורה כאשר מפתחים משתמשים באתחול אסינכרוני ברמה העליונה — הדפדפן פשוט לא "יראה" את ה-handler בהפעלה הראשונה של ה-SW. כתוצאה מכך, ההרחבה מפסיקה להגיב לפעולות משתמש.
אנחנו מפתחים הרחבות דפדפן כבר למעלה מחמש שנים והעברנו יותר מ-20 פרויקטים עם Service Workers. הניסיון שלנו מבטיח יציבות וביצועים גם בתרחישים מורכבים. אם אתם רוצים שההרחבה שלכם תעבוד ללא תקלות, צרו קשר לייעוץ.
מדוע ה-Service Worker מופסק?
הדפדפן מפעיל את ה-SW כאשר:
- ההרחבה מותקנת/מתעדכנת
- מתקבלת הודעה מ-content script או מ-popup
- מופעל alarm
- מתרחש אירוע רשת (אם נרשמתם אליו)
לאחר שכל ה-handlers מסיימים, הדפדפן עלול להרוג את התהליך לאחר כ-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 });
}); כיצד להבטיח שמירת נתונים?
כלל ראשון: אל תסתמכו על משתנים גלובליים. השתמשו ב-// ПЛОХО — состояние потеряется при перезапуске 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 לאחסון מצב. שנית, עבור פעולות שחייבות להיות אטומיות, יש ליישם גישה טרנזקציונית עם תור. זה מבטיח שאף משימה לא תלך לאיבוד גם עם הפסקה פתאומית של ה-SW.
מדוע רישום אירועים סינכרוני חשוב?
יש לרשום handlers באופן סינכרוני ברמה העליונה. אחרת, הדפדפן עלול לא "לראות" אותם בעת הפעלת ה-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 (רקע קבוע) | MV3 (service worker) |
|---|---|---|
| מחזור חיים | תהליך קבוע | מופסק במצב סרק |
| מצב גלובלי | עובד | מתאפס באתחול מחדש |
| setTimeout/setInterval | אמין | לא אמין (השתמשו ב-alarms) |
| צריכת זיכרון | תמיד פעיל | רק בעת הצורך |
| מהירות טעינה | גבוהה יותר | נמוכה יותר (הפעלה עצלה) |
MV3 מפחית את צריכת הזיכרון פי 2 בהשוואה ל-MV2, ואת זמן ההפעלה של ההרחבה ב-30% הודות לטעינה עצלה. החיסכון בתשתית השרת יכול להגיע ל-40%.
חיבורים ארוכי טווח באמצעות Port
למשימות שנמשכות יותר מכמה שניות (סטרימינג, polling), השתמשו ב-chrome.storage.sync. חיבור פעיל שומר על ה-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 למשימות תקופתיות
// 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-ответов } ו-chrome.runtime.connect() אינם אמינים — הם לא שורדים אתחול מחדש. השתמשו ב-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 עלול להיהרג באמצע פעולה אסינכרונית. עבור פעולות קריטיות, השתמשו בגישה טרנזקציונית עם תור ב-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.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 }); } }); });ובדקו את מצב ה-SW. - ודאו שאירועים נרשמים באופן סינכרוני (השתמשו ב-
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(); } });ברמה העליונה). - בדקו שכל ה-handlers (
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 }); } },console.log,onInstalled) מופיעים ביומנים. - בצעו אמולציה של אתחול מחדש של ה-SW באמצעות
onMessage(קראו ל-onAlarmאו טענו מחדש את ההרחבה). - ודאו שהנתונים משוחזרים מ-chrome.storage לאחר אתחול מחדש.
מה תקבלו כתוצאה?
- Service Worker עובד במלואו עם רישום אירועים נכון.
- העברה מ-MV2 ל-MV3 ללא אובדן פונקציונליות.
- אופטימיזציה של ביצועים: הפחתת צריכת זיכרון פי 2.
- אחסון מצב אמין באמצעות chrome.storage ותורים.
- תיעוד ארכיטקטורה והוראות תחזוקה.
- הבטחת פעולה יציבה: אנחנו אחראים לתוצאה.
שלבי פיתוח עבור SW
| שלב | משך (הערכה) |
|---|---|
| ביקורת של ההרחבה הנוכחית | 1–2 ימים |
| עיצוב ארכיטקטורה | 2–3 ימים |
| פיתוח ובדיקות | 5–10 ימים |
| תיעוד ובדיקות סופיות | 1–2 ימים |
| תמיכה לאחר השקה | בהסכמה |
למה להפקיד בידינו את הפיתוח?
יש לנו שנים של ניסיון ביצירת הרחבות דפדפן — למעלה מ-5 שנים בשוק, עשרות פרויקטים מוצלחים. המהנדסים שלנו מוסמכים ומבינים לעומק את הניואנסים של Manifest V3 ו-Service Workers. אנחנו מבטיחים פעולה יציבה של ההרחבה שלכם גם בתרחישים מורכבים. הזמינו ביקורת של ההרחבה שלכם — נזהה אזורים בעייתיים ונציע תוכנית העברה. קבלו ייעוץ על ארכיטקטורת Service Worker מהמהנדסים שלנו.
צרו קשר להערכת הפרויקט שלכם.







