טיפול באירועים עם קובצי שירות (service worker)

מדריך בנושא מושגים של קובצי שירות בתוסף (service worker)

סקירה כללית

המדריך הזה הוא מבוא ל-Chrome Extension Service worker. כחלק מהשינוי הזה המדריך הזה יבנה תוסף שיאפשר למשתמשים לנווט במהירות להפניה אוטומטית של Chrome API דפים באמצעות סרגל הכתובות. תלמדו איך:

  • רישום Service Worker וייבוא מודולים.
  • ניפוי באגים בקובץ השירות של התוסף.
  • לנהל אירועים ולטפל באירועים.
  • הפעלת אירועים תקופתיים.
  • תקשורת עם סקריפטים של תוכן.

לפני שמתחילים

המדריך הזה מניח שיש לכם ניסיון בסיסי בפיתוח אתרים. מומלץ לבדוק תוספים 101 ו-Hello World להיכרות עם בפיתוח תוספים.

בניית התוסף

מתחילים ביצירת ספרייה חדשה בשם quick-api-reference לשמירת קובצי התוספים, או מורידים את קוד המקור ממאגר הדוגמאות של GitHub.

שלב 1: רישום Service Worker

יוצרים את קובץ המניפסט ברמה הבסיסית (root) של הפרויקט ומוסיפים את הקוד הבא:

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 כמודול 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-bar.js:

console.log("sw-omnibox.js");

sw-tips.js:

console.log("sw-tips.js");

במאמר ייבוא סקריפטים אפשר למצוא מידע על דרכים אחרות לייבוא קבצים מרובים ב-Service Worker.

אופציונלי: ניפוי באגים ב-Service Worker

אסביר איך למצוא את היומנים של Service Worker ולדעת מתי הוא הסתיים. קודם כול, פועלים לפי ההוראות לטעינת תוסף שלא ארוז.

אחרי 30 שניות יופיע Service Worker (לא פעיל) כלומר, Service Worker הסתיים. לוחצים על 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 chrome.storage כדי לשמור את המצבים בסשנים שונים של קובצי שירות (service worker). כדי לקבל גישה לאחסון, אנחנו צריכים לבקש הרשאה במניפסט:

manifest.json:

{
  ...
  "permissions": ["storage"],
}

קודם כול, שומרים את הצעות ברירת המחדל באחסון. אפשר להפעיל את המצב כשהתוסף מותקן לראשונה על ידי האזנה לאירוע runtime.onInstalled():

sw-bar.js:

...
// Save default API suggestions
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.local.set({
      apiSuggestions: ['tabs', 'storage', 'scripting']
    });
  }
});

ל-Service Worker אין גישה ישירה לאובייקט window ולכן הם לא יכולים להשתמש window.localStorage לשמירת ערכים. בנוסף, קובצי שירות (service worker) הם סביבות ביצוע לטווח קצר; הם נסגרים שוב ושוב במהלך הסשן של המשתמש בדפדפן, ולכן הם לא תואמים משתנים גלובליים. במקום זאת, משתמשים ב-chrome.storage.local שמאחסן נתונים במחשב המקומי.

ראו נתונים מתמידים במקום שימוש במשתנים גלובליים כדי לקבל מידע נוסף על אפשרויות אחסון אחרות לעובדים בשירות תוספים.

שלב 5: רושמים את האירועים

כל ה-event listener צריכים להיות רשומים באופן סטטי בהיקף הגלובלי של ה-Service Worker. במילים אחרות, פונקציות event listener לא יכולות להיות מקוננות בפונקציות אסינכרוניות. כך Chrome יוכל להבטיח שכל הגורמים שמטפלים באירועים ישוחזרו במקרה של הפעלה מחדש של Service Worker.

בדוגמה הזו נשתמש ב-API chrome.omnibox, אבל קודם צריך להצהיר במניפסט על הטריגר של מילת המפתח בסרגל הכתובות:

manifest.json:

{
  ...
  "minimum_chrome_version": "102",
  "omnibox": {
    "keyword": "api"
  },
}

עכשיו צריך לרשום את פונקציות event listener בסרגל הכתובות ברמה העליונה של הסקריפט. כשהמשתמש יזין את מילת המפתח בסרגל הכתובות (api) בסרגל הכתובות ואחריה כרטיסייה או רווח, Chrome יציג רשימת הצעות שמבוססת על מילות המפתח באחסון. האירוע onInputChanged(), שמקבל את הקלט הנוכחי של המשתמש ואובייקט suggestResult, אחראי לאכלוס ההצעות האלה.

sw-bar.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
chrome.omnibox.onInputChanged.addListener(async (input, suggest) => {
  await chrome.omnibox.setDefaultSuggestion({
    description: 'Enter a Chrome API or choose from past searches'
  });
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  const suggestions = apiSuggestions.map((api) => {
    return { content: api, description: `Open chrome.${api} API` };
  });
  suggest(suggestions);
});

אחרי שהמשתמש יבחר הצעה, הסמל onInputEntered() יפתח את דף העזר התואם של Chrome API.

sw-bar.js:

...
// Open the reference page of the chosen API
chrome.omnibox.onInputEntered.addListener((input) => {
  chrome.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input });
  // Save the latest keyword
  updateHistory(input);
});

הפונקציה updateHistory() מקבלת את הקלט מסרגל הכתובות ושומרת אותו ב-storage.local. כך ניתן להשתמש במונח החיפוש האחרון כהצעה בסרגל הכתובות.

sw-bar.js:

...
async function updateHistory(input) {
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  apiSuggestions.unshift(input);
  apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES);
  return chrome.storage.local.set({ apiSuggestions });
}

שלב 6: הגדרת אירוע חוזר

השיטות setTimeout() או setInterval() משמשות בדרך כלל לביצוע עיכובים או פעולות תקופתיות למשימות סיווג. עם זאת, ממשקי ה-API האלה עלולים להיכשל כי המתזמן יבטל את הטיימרים כשהשירות הסתיים. במקום זאת, התוספים יכולים להשתמש ב-API של chrome.alarms.

כדי להתחיל, צריך לבקש את ההרשאה "alarms" במניפסט. בנוסף, כדי לאחזר את הטיפים לתוספים ממיקום מתארח מרחוק, צריך לבקש הרשאת מארח:

manifest.json:

{
  ...
  "permissions": ["storage"],
  "permissions": ["storage", "alarms"],
  "host_permissions": ["https://extension-tips.glitch.me/*"],
}

התוסף יאחזר את כל הטיפים, יבחר אחד באופן אקראי וישמור אותו באחסון. אנחנו ניצור התראה שתופעל פעם ביום כדי לעדכן את הטיפ. התראות לא נשמרות כשסוגרים את Chrome. לכן אנחנו צריכים לבדוק אם ההתראה קיימת וליצור אותה אם היא לא קיימת.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://extension-tips.glitch.me/tips.json');
  const tips = await response.json();
  const randomIndex = Math.floor(Math.random() * tips.length);
  return chrome.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 chrome.alarms.get(ALARM_NAME);
  if (typeof alarm === 'undefined') {
    chrome.alarms.create(ALARM_NAME, {
      delayInMinutes: 1,
      periodInMinutes: 1440
    });
    updateTip();
  }
}

createAlarm();

// Update tip once a day
chrome.alarms.onAlarm.addListener(updateTip);

שלב 7: תקשורת עם הקשרים אחרים

תוספים משתמשים בסקריפטים של תוכן כדי לקרוא ולשנות את תוכן הדף. כשמשתמש מבקר בדף עזר של Chrome API, סקריפט התוכן של התוסף יעדכן את הדף עם הטיפ היומי. ה-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 chrome.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;
}

השלב האחרון הוא להוסיף handler של הודעות ל-Service Worker ששולח תשובה לסקריפט התוכן עם הטיפ היומי.

sw-tips.js:

...
// Send tip to content script via messaging
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.greeting === 'tip') {
    chrome.storage.local.get('tip').then(sendResponse);
    return true;
  }
});

בדיקה שזה עובד

מוודאים שמבנה הקובץ של הפרויקט נראה כך:

התוכן של תיקיית התוספים: תיקיית התמונות, asset.json,, service-worker.js, sw- Display.js, sw-tips.js,
ו-content.js

טעינה מקומית של התוסף

כדי לטעון תוסף unpacked במצב פיתוח, פועלים לפי השלבים שמפורטים בקטע Hello World.

פתיחת דף עזר

  1. צריך להזין את מילת המפתח 'api' בסרגל הכתובות של הדפדפן.
  2. לוחצים על Tab או 'רווח'.
  3. צריך להזין את השם המלא של ה-API.
    • או בחירה מתוך רשימה של חיפושים קודמים
  4. ייפתח דף חדש עם דף העזר של Chrome API.

זה אמור להיראות כך:

פתיחת ההפניה ל-API מהירה בעזרת הפניה לממשק ה-API של זמן הריצה
תוסף Quick API פותח את ה-Runtime API.

פתיחת הטיפ היומי

לוחצים על לחצן הטיפ שנמצא בסרגל הניווט כדי לפתוח את הטיפ לתוסף.

פתיחת הטיפ היומי ב-
תוסף API מהיר פותח את הטיפ היומי.

🎯 שיפורים פוטנציאליים

בהתאם למה שלמדתם היום, נסו לבצע אחת מהפעולות הבאות:

  • מגלים דרך נוספת ליישם את ההצעות בסרגל הכתובות.
  • יצירת מודל מותאם אישית להצגת הטיפ לתוסף.
  • פתיחת דף נוסף לדפי העזר של ה-API לתוספים לאינטרנט ב-MDN.

ממשיכים לבנות!

כל הכבוד, סיימת את המדריך 🎉. אפשר להמשיך ולשפר את המיומנויות שלך מדריכים למתחילים:

Extension מה תלמדו
זמן קריאה כדי להוסיף רכיב לקבוצת דפים ספציפית באופן אוטומטי.
מנהל הכרטיסיות כדי ליצור חלון קופץ שמנהל את הכרטיסיות בדפדפן.
מצב ריכוז כדי להריץ קוד בדף הנוכחי אחרי שלוחצים על פעולת התוסף.

מידע נוסף על היעדים

כדי להמשיך את תוכנית הלימודים של קובצי שירות התוספים, מומלץ לעיין במאמרים הבאים: