הדרכה שכוללת הסברים על מושגים שקשורים ל-service worker של תוספים
סקירה כללית
במדריך הזה מוסבר על Service Workers של תוספי Chrome. במסגרת המדריך הזה, תיצרו תוסף שיאפשר למשתמשים לנווט במהירות לדפי העיון ב-Chrome API באמצעות תיבת הכתובת. בשיעור הזה תלמדו איך:
- רושמים את קובץ השירות (service worker) ומייבאים מודולים.
- מבצעים ניפוי באגים ב-service worker של התוסף.
- ניהול מצב וטיפול באירועים.
- הפעלת אירועים תקופתיים.
- תקשורת עם סקריפטים של תוכן.
לפני שמתחילים
במדריך הזה אנחנו יוצאים מנקודת הנחה שיש לכם ניסיון בסיסי בפיתוח אתרים. מומלץ לעיין במאמרים Extensions 101 ו-Hello World כדי לקבל מבוא לפיתוח תוספים.
יצירת התוסף
מתחילים ביצירת ספרייה חדשה בשם quick-api-reference כדי לאחסן את קובצי התוסף, או מורידים את קוד המקור ממאגר הדוגמאות שלנו ב-GitHub.
שלב 1: רישום של Service Worker
יוצרים את קובץ manifest ברמה הבסיסית של הפרויקט ומוסיפים את הקוד הבא:
manifest.json:
{
"manifest_version": 3,
"name": "Open extension API reference",
"version": "1.0.0",
"icons": {
"16": "images/icon-16.png",
"128": "images/icon-128.png"
},
"background": {
"service_worker": "service-worker.js"
}
}
תוספים רושמים את ה-service worker שלהם במניפסט, שכולל רק קובץ JavaScript יחיד.
אין צורך להתקשר אל navigator.serviceWorker.register(), כמו בדף אינטרנט.
יוצרים תיקייה בשם images ואז מורידים את הסמלים לתוכה.
במאמר השלבים הראשונים במדריך בנושא זמן קריאה אפשר לקרוא מידע נוסף על המטא-נתונים ועל הסמלים של התוסף במניפסט.
שלב 2: ייבוא של כמה מודולים של Service Worker
ה-service worker שלנו מטמיע שתי תכונות. כדי לשפר את יכולת התחזוקה, נטמיע כל תכונה במודול נפרד. קודם צריך להצהיר על ה-service worker כמודול ES במניפסט, כדי שנוכל לייבא מודולים ל-service worker:
manifest.json:
{
"background": {
"service_worker": "service-worker.js",
"type": "module"
},
}
יוצרים את הקובץ service-worker.js ומייבאים שני מודולים:
import './sw-omnibox.js';
import './sw-tips.js';
יוצרים את הקבצים האלה ומוסיפים לכל אחד מהם יומן מסוף.
sw-omnibox.js:
console.log("sw-omnibox.js");
sw-tips.js:
console.log("sw-tips.js");
במאמר ייבוא סקריפטים מוסבר על דרכים אחרות לייבוא של כמה קבצים ב-Service Worker.
אופציונלי: ניפוי באגים בקובץ שירות (Service Worker)
אני אסביר איך למצוא את היומנים של Service Worker ואיך לדעת מתי הוא הסתיים. קודם כול, פועלים לפי ההוראות לטעינת תוסף לא ארוז.
אחרי 30 שניות תופיע ההודעה 'service worker (inactive)', כלומר, ה-service worker הסתיים. כדי לבדוק את ה-service worker, לוחצים על הקישור service worker (inactive). אפשר לראות את זה באנימציה הבאה.
שמת לב שכשבודקים את קובץ השירות (service worker) הוא מתעורר? פתיחת ה-service worker בכלי הפיתוח תשמור אותו פעיל. כדי לוודא שהתוסף פועל בצורה תקינה כש-service worker מסתיים את הפעולה, חשוב לזכור לסגור את כלי הפיתוח.
עכשיו, נשבור את התוסף כדי ללמוד איפה נמצאות השגיאות. אחת הדרכים לעשות זאת היא למחוק את .js מהייבוא './sw-omnibox.js' בקובץ service-worker.js. ל-Chrome לא תהיה אפשרות לרשום את קובץ השירות (service worker).
חוזרים אל chrome://extensions ומרעננים את התוסף. יוצגו שתי שגיאות:
Service worker registration failed. Status code: 3.
An unknown error occurred when fetching the script.
במאמר ניפוי באגים בתוספים מוסברות דרכים נוספות לניפוי באגים ב-Service Worker של התוסף.
שלב 4: מאתחלים את המצב
דפדפן Chrome ישבית את העובדים של השירות אם הם לא נדרשים. אנחנו משתמשים ב-API browser.storage כדי לשמור את המצב בסשנים של Service Worker. כדי לקבל גישה לאחסון, צריך לבקש הרשאה במניפסט:
manifest.json:
{
...
"permissions": ["storage"],
}
קודם צריך לשמור את ההצעות שמוגדרות כברירת מחדל באחסון. אפשר לאתחל את המצב כשהתוסף מותקן בפעם הראשונה על ידי האזנה לאירוע runtime.onInstalled():
sw-omnibox.js:
...
// Save default API suggestions
browser.runtime.onInstalled.addListener(({ reason }) => {
if (reason === 'install') {
browser.storage.local.set({
apiSuggestions: ['tabs', 'storage', 'scripting']
});
}
});
ל-Service workers אין גישה ישירה לאובייקט החלון, ולכן הם לא יכולים להשתמש ב-window.localStorage כדי לאחסן ערכים. בנוסף, service workers הם סביבות הרצה לטווח קצר. הם מסיימים את הפעולה שלהם שוב ושוב במהלך סשן הדפדפן של המשתמש, ולכן הם לא תואמים למשתנים גלובליים. במקום זאת, צריך להשתמש ב-browser.storage.local ששומר נתונים במכונה המקומית.
במאמר שמירת נתונים במקום שימוש במשתנים גלובליים מוסבר על אפשרויות אחסון אחרות ל-service workers של תוספים.
שלב 5: רושמים את האירועים
צריך לרשום את כל מאזיני האירועים באופן סטטי בהיקף הגלובלי של ה-service worker. במילים אחרות, אסור להטמיע מאזינים לאירועים בפונקציות אסינכרוניות. כך Chrome יכול לוודא שכל הגורמים שמטפלים באירועים ישוחזרו במקרה של הפעלה מחדש של קובץ שירות.
בדוגמה הזו נשתמש ב-API browser.omnibox, אבל קודם צריך להצהיר על מילת המפתח להפעלת סרגל הכתובות במניפסט:
manifest.json:
{
...
"minimum_chrome_version": "102",
"omnibox": {
"keyword": "api"
},
}
עכשיו, רושמים את רכיבי event listener של סרגל הכתובות ברמה העליונה של הסקריפט. כשמשתמש מזין את מילת המפתח של סרגל הכתובות (api) בסרגל הכתובות ואחריה מקש Tab או רווח, Chrome יציג רשימה של הצעות שמבוססות על מילות המפתח ששמורות באחסון. האירוע onInputChanged(), שמקבל את קלט המשתמש הנוכחי ואובייקט suggestResult, אחראי לאכלוס ההצעות האלה.
sw-omnibox.js:
...
const URL_CHROME_EXTENSIONS_DOC =
'https://developer.chrome.com/docs/extensions/reference/';
const NUMBER_OF_PREVIOUS_SEARCHES = 4;
// Display the suggestions after user starts typing
browser.omnibox.onInputChanged.addListener(async (input, suggest) => {
await browser.omnibox.setDefaultSuggestion({
description: 'Enter a Chrome API or choose from past searches'
});
const { apiSuggestions } = await browser.storage.local.get('apiSuggestions');
const suggestions = apiSuggestions.map((api) => {
return { content: api, description: `Open browser.${api} API` };
});
suggest(suggestions);
});
אחרי שהמשתמש בוחר הצעה, onInputEntered() יפתח את דף העיון המתאים ב-Chrome API.
sw-omnibox.js:
...
// Open the reference page of the chosen API
browser.omnibox.onInputEntered.addListener((input) => {
browser.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input });
// Save the latest keyword
updateHistory(input);
});
הפונקציה updateHistory() מקבלת את הקלט של סרגל הכתובות ושומרת אותו ב-storage.local. כך אפשר להשתמש במונח החיפוש האחרון מאוחר יותר כהצעה בסרגל הכתובות.
sw-omnibox.js:
...
async function updateHistory(input) {
const { apiSuggestions } = await browser.storage.local.get('apiSuggestions');
apiSuggestions.unshift(input);
apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES);
return browser.storage.local.set({ apiSuggestions });
}
שלב 6: הגדרת אירוע חוזר
בדרך כלל משתמשים בשיטות setTimeout() או setInterval() כדי לבצע משימות מושהות או תקופתיות. עם זאת, יכול להיות שה-API האלה ייכשלו כי המתזמן יבטל את הטיימרים כשה-service worker יופסק. במקום זאת, תוספים יכולים להשתמש ב-API browser.alarms.
כדי להתחיל, צריך לבקש את ההרשאה "alarms" במניפסט:
manifest.json:
{
...
"permissions": ["storage"],
"permissions": ["storage", "alarms"],
}
התוסף יאחזר את כל הטיפים, יבחר אחד באופן אקראי וישמור אותו באחסון. אנחנו ניצור התראה שתופעל פעם ביום כדי לעדכן את הטיפ. השעונים המעוררים לא נשמרים כשסוגרים את Chrome. לכן אנחנו צריכים לבדוק אם השעון המעורר קיים, ואם לא, ליצור אותו.
sw-tips.js:
// Fetch tip & save in storage
const updateTip = async () => {
const response = await fetch('https://chrome.dev/f/extension_tips/');
const tips = await response.json();
const randomIndex = Math.floor(Math.random() * tips.length);
return browser.storage.local.set({ tip: tips[randomIndex] });
};
const ALARM_NAME = 'tip';
// Check if alarm exists to avoid resetting the timer.
// The alarm might be removed when the browser session restarts.
async function createAlarm() {
const alarm = await browser.alarms.get(ALARM_NAME);
if (typeof alarm === 'undefined') {
browser.alarms.create(ALARM_NAME, {
delayInMinutes: 1,
periodInMinutes: 1440
});
updateTip();
}
}
createAlarm();
// Update tip once a day
browser.alarms.onAlarm.addListener(updateTip);
שלב 7: תקשורת עם הקשרים האחרים
תוספים משתמשים בסקריפטים של תוכן כדי לקרוא ולשנות את התוכן של הדף. כשמשתמש נכנס לדף מאמרי העזרה של ה-API של Chrome, סקריפט התוכן של התוסף מעדכן את הדף עם טיפ היום. הוא שולח הודעה כדי לבקש את הטיפ היומי מ-Service Worker.
מתחילים בהצהרה על סקריפט התוכן במניפסט ומוסיפים את תבנית ההתאמה שמתאימה למסמכי העזרה של Chrome API.
manifest.json:
{
...
"content_scripts": [
{
"matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
"js": ["content.js"]
}
]
}
יוצרים קובץ תוכן חדש. הקוד הבא שולח הודעה אל Service Worker ומבקש את הטיפ. לאחר מכן, מוסיף כפתור שפותח חלון קופץ עם הטיפ לשימוש בתוסף. הקוד הזה משתמש ב-Popover API החדש של פלטפורמת האינטרנט.
content.js:
(async () => {
// Sends a message to the service worker and receives a tip in response
const { tip } = await browser.runtime.sendMessage({ greeting: 'tip' });
const nav = document.querySelector('.upper-tabs > nav');
const tipWidget = createDomElement(`
<button type="button" popovertarget="tip-popover" popovertargetaction="show" style="padding: 0 12px; height: 36px;">
<span style="display: block; font: var(--devsite-link-font,500 14px/20px var(--devsite-primary-font-family));">Tip</span>
</button>
`);
const popover = createDomElement(
`<div id='tip-popover' popover style="margin: auto;">${tip}</div>`
);
document.body.append(popover);
nav.append(tipWidget);
})();
function createDomElement(html) {
const dom = new DOMParser().parseFromString(html, 'text/html');
return dom.body.firstElementChild;
}
השלב האחרון הוא להוסיף ל-service worker שלנו פונקציית טיפול בהודעות, שתשלח תשובה לסקריפט התוכן עם הטיפ היומי.
sw-tips.js:
...
// Send tip to content script via messaging
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.greeting === 'tip') {
browser.storage.local.get('tip').then(sendResponse);
return true;
}
});
בדיקה שהכול עובד
מוודאים שמבנה הקבצים של הפרויקט נראה כך:

טעינת התוסף באופן מקומי
כדי לטעון תוסף מסוג unpacked במצב פיתוח, פועלים לפי השלבים במאמר Hello world.
פתיחת דף עזר
- מזינים את מילת המפתח api בסרגל הכתובות בדפדפן.
- מקישים על Tab או על מקש הרווח.
- מזינים את השם המלא של ה-API.
- או בוחרים מתוך רשימת חיפושים קודמים
- ייפתח דף חדש עם הפניה למאמרי העזרה של ה-API של Chrome.
הוא אמור להיראות כך:
פתיחת העצה היומית
לוחצים על לחצן הטיפ בסרגל הניווט כדי לפתוח את הטיפ של התוסף.
🎯 שיפורים אפשריים
על סמך מה שלמדת היום, נסה לבצע אחת מהמשימות הבאות:
- אפשר לנסות דרך אחרת להטמיע את ההצעות בסרגל הכתובות.
- יוצרים חלון קופץ מותאם אישית להצגת הטיפ לתוסף.
- פותחים דף נוסף בדפי ה-API של MDN Web Extensions.
ממשיכים לבנות!
כל הכבוד, סיימת את המדריך הזה 🎉. כדי להמשיך לשפר את הכישורים שלך, כדאי לעבור על מדריכים נוספים למתחילים:
| Extension | מה תלמדו |
|---|---|
| זמן קריאה | כדי להוסיף רכיב לקבוצה מסוימת של דפים באופן אוטומטי. |
| Tabs Manager | כדי ליצור חלון קופץ שמנהל כרטיסיות בדפדפן. |
| מצב פוקוס | כדי להריץ קוד בדף הנוכחי אחרי שלוחצים על פעולת התוסף. |
עוד מידע
כדי להמשיך בתוכנית הלימודים בנושא Service Worker של תוספים, מומלץ לעיין במאמרים הבאים: