כתיבת סקריפט של קובץ שירות (service worker)

מעבדת הקוד הזו היא חלק מקורס ההדרכה של Progressive Web Apps, שפותח על ידי צוות ההדרכה של Google Developers. אם תשלימו את הקוד של השיעורים השונים ברצף, תוכלו להפיק את המקסימום מהקורס הזה.

לפרטים מלאים על הקורס, מומלץ לעיין בסקירה הכללית של Progressive Web Apps.

מבוא

בשיעור ה-Lab הזה תלמדו איך ליצור קובץ שירות פשוט (service worker) ולהסביר את מחזור החיים של קובץ השירות (service worker).

מה תלמדו

  • יצירת סקריפט בסיסי של קובץ שירות (service worker), התקנה שלו וביצוע פשוט של ניפוי באגים

מה חשוב לדעת

  • JavaScript ו-HTML בסיסי
  • קונספטים ותחביר בסיסי של ES2015 Promis
  • איך מפעילים את מסוף המפתחים

מה צריך לפני שמתחילים?

מורידים או משכפלים את מאגר pwa-training-labs מ-github ומתקינים את גרסת LTS של Node.js, אם צריך.

עוברים לספרייה service-worker-lab/app/ ופותחים שרת פיתוח מקומי:

cd service-worker-lab/app
npm install
node server.js

אפשר לסיים את השרת בכל שלב עם Ctrl-c.

פותחים את הדפדפן ועוברים אל localhost:8081/.

הערה: יש לבטל את הרישום של כל מופעי השירות (service worker) ולנקות את כל המטמון של קובץ השירות (service worker) ב-localhost כדי שלא יפריעו למעבדה. כדי לעשות זאת בכלי הפיתוח של Chrome, לוחצים על ניקוי נתוני האתר בקטע ניקוי האחסון בכרטיסייה אפליקציה.

פותחים את התיקייה service-worker-lab/app/ בעורך הטקסט המועדף עליכם. התיקייה app/ היא המקום שבו תיצרו את שיעור ה-Lab.

התיקייה הזו מכילה:

  • below/another.html, js/another.js, js/other.js ו-other.html הם משאבים לדוגמה שבהם אנחנו משתמשים כדי לערוך ניסויים בהיקף של קובץ שירות (service worker)
  • התיקייה styles/ מכילה את הגיליונות האלקטרוניים המדורגים לשיעור ה-Lab הזה
  • תיקייה אחת (test/) מכילה קבצים לבדיקת ההתקדמות שלך
  • index.html הוא דף ה-HTML הראשי של האתר/האפליקציה לדוגמה שלנו
  • service-worker.js הוא קובץ JavaScript המשמש ליצירת קובץ השירות (service worker)
  • package.json וגם package-lock.json עוקבים אחר חבילות הצומת שבהן נעשה שימוש בפרויקט הזה
  • server.js הוא שרת פשוט של אקספרס שבו אנחנו משתמשים לאירוח האפליקציה שלנו

יש לפתוח את service-worker.js בעורך הטקסט. שימו לב שהקובץ ריק. עדיין לא הוספנו אף קוד להפעלה בתוך קובץ השירות (service worker).

יש לפתוח את index.html בעורך הטקסט.

בתוך תגי <script>, יש להוסיף את הקוד הבא כדי לרשום את קובץ השירות (service worker):

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('Service Worker is registered', registration);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

שומרים את הסקריפט ומרעננים את הדף. המסוף אמור להחזיר הודעה שמציינת ש-service worker רשום. ב-Chrome, אתם יכולים לבדוק אם קובץ השירות (service worker) רשום. לשם כך, פותחים את כלי הפיתוח (Control + Shift + I ב-Windows ו-Linux, או את ⌘ + Alt + I ב-Mac), לוחצים על הכרטיסייה 'אפליקציה' ואז על האפשרות Service Workers. אתם אמורים לראות משהו כזה:

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

הסבר

הקוד שלמעלה רושם את הקובץ service-worker.js כ-worker (service worker). תחילה הוא בודק אם הדפדפן תומך ב-service worker. יש לבצע את הפעולה הזו בכל פעם שאתם רושמים קובץ שירות (service worker), כי ייתכן שחלק מהדפדפנים לא תומכים ב-worker (service worker). הקוד רושם את קובץ השירות (service worker) באמצעות שיטת register של ה-ServiceWorkerContainer API, הכלול בממשק Navigator של החלון.

navigator.serviceWorker.register(...) מחזירה הבטחה שנפתרת עם אובייקט registration לאחר ש-worker (service worker) נרשם בהצלחה. אם הרישום ייכשל, ההבטחה תידחה.

שינויים באירועי ההפעלה של קובץ השירות (service worker)

הוספת פונקציות event listener

יש לפתוח את service-worker.js בעורך הטקסט.

אתם צריכים להוסיף ל-service worker את הפונקציות הבאות של האירוע:

self.addEventListener('install', event => {
  console.log('Service worker installing...');
  // Add a call to skipWaiting here
});

self.addEventListener('activate', event => {
  console.log('Service worker activating...');
});

שומרים את הקובץ.

יש להסיר ידנית את קובץ השירות (service worker) ולרענן את הדף כדי להתקין ולהפעיל את קובץ השירות (service worker) הזה. יומן המסוף צריך לציין ש-service worker החדש נרשם, הופעל והופעל.

הערה: ייתכן שיומן הרישום לא יופיע בסדר עם היומנים האחרים (התקנה והפעלה). קובץ השירות (service worker) פועל בו-זמנית עם הדף, כך שאנחנו לא יכולים להבטיח שהיומנים יוצגו ביומן (יומן הרישום מגיע מהדף, בעוד שיומני ההתקנה וההפעלה מגיעים מה-worker). עם זאת, אירועי התקנה, הפעלה ואירועים אחרים של השירות (service worker) מתקיימים בסדר מוגדר בתוך קובץ השירות (service worker), והם צריכים תמיד להופיע בסדר הצפוי.

הסבר

קובץ השירות (service worker) פולט אירוע install בסוף הרישום. בקוד שלמעלה, הודעה מתועדת בתוך event listener של install, אבל באפליקציה בעולם האמיתי זה מקום טוב לשמירה במטמון של נכסים סטטיים.

כאשר קובץ שירות (service worker) רשום, הדפדפן מזהה אם קובץ השירות (service worker) חדש (אם הוא שונה מ-service worker שהותקן בעבר) או מפני שאין קובץ שירות (service worker) רשום לאתר הזה. אם קובץ השירות (service worker) חדש (כפי שהוא במקרה זה), הדפדפן יתקין אותו.

סקריפט השירות (service worker) פולט אירוע activate כאשר הוא שולט בדף. הקוד שלמעלה מתעד כאן הודעה, אבל האירוע הזה משמש לעיתים קרובות לעדכון מטמון.

רק קובץ שירות אחד (service worker) יכול להיות פעיל בכל זמן נתון (יש לעיין בהיקף קובץ השירות (service worker), לכן קובץ שירות (service worker) חדש שהותקן לא יופעל עד שהשימוש בו כבר לא יהיה בשימוש. לכן יש לסגור את כל הדפים שבשליטת קובץ שירות (service worker) לפני ש-worker חדש יוכל להשתלט עליו. מאחר שביטלנו את הרישום של קובץ השירות (service worker) הקיים, קובץ השירות (service worker) החדש הופעל באופן מיידי.

הערה: רענון הדף אינו מספיק לצורך העברת השליטה של קובץ השירות (service worker) החדש, מפני שהדף החדש יידרש לפני טעינת הדף הנוכחי, ולא יהיה זמן שבו קובץ השירות הישן לא יהיה בשימוש.

הערה: אפשר גם להפעיל ידנית קובץ שירות (service worker) חדש באמצעות חלק מהדפדפנים' דפדפנים פרוגרמטיים ובאמצעות skipWaiting(), כפי שמתואר בסעיף 3.4.

עדכון קובץ השירות (service worker)

אפשר להוסיף את התגובה הבאה לכל מקום בservice-worker.js:

// I'm a new service worker

שומרים את הקובץ ומרעננים את הדף. מעיינים ביומנים שבמסוף. שימו לב שה-service worker החדש מותקן אבל לא מופעל. ב-Chrome, אתם יכולים לראות את קובץ השירות (service worker) בהמתנה בכרטיסייה Application (אפליקציה) בכלי הפיתוח.

סגור את כל הדפים המשויכים ל-worker (service worker). לאחר מכן, פותחים מחדש את localhost:8081/. יומן המסוף צריך לציין ש-service worker החדש הופעל.

הערה: אם אתם מקבלים תוצאות בלתי צפויות, יש לוודא שמטמון ה-HTTP מושבת בכלים למפתחים.

הסבר

הדפדפן מזהה הבדלים בבייטים בין הקובץ החדש של קובץ השירות (service worker) (בגלל התגובה שהוספת) ולכן קובץ ה-service worker החדש מותקן. מאחר שרק קובץ שירות אחד (service worker) יכול להיות פעיל בכל זמן (בטווח נתון), אמנם קובץ השירות (service worker) החדש מותקן, אבל הוא לא יהיה פעיל עד שלא ייעשה בו יותר שימוש ב-worker (service worker) הנוכחי. סגירת כל הדפים במסגרת הבקרה של קובץ השירות (service worker) הישן יכולה להפעיל את קובץ השירות (service worker) החדש.

דילוג על שלב ההמתנה

אתם יכולים לדלג על שלב ההמתנה, כדי שה-worker החדש יוכל לפעול מיד, גם אם קיים קובץ שירות (service worker) קיים.

ב-service-worker.js, יש להוסיף שיחה אל skipWaiting ב-event listener של install:

self.skipWaiting();

שומרים את הקובץ ומרעננים את הדף. הערה: קובץ השירות (service worker) החדש מותקן ומופעל באופן מיידי, גם אם קובץ השירות (service worker) הקודם היה בשליטתו.

הסבר

השיטה skipWaiting() מאפשרת ל-worker (service worker) לפעול מיד עם סיום ההתקנה. event listener של אירוע הוא מקום נפוץ לביצוע השיחה ב-skipWaiting(), אבל אפשר להפעיל אותו בכל שלב בשלב ההמתנה או לפניו. בתיעוד הזה מוסבר איך ומתי להשתמש ב-skipWaiting(). בהמשך שיעור ה-Lab, נוכל לבדוק קוד חדש של קובץ שירות (service worker) בלי לבטל ידנית את רישום השירות (service worker).

למידע נוסף

קובצי שירות (service worker) יכולים לפעול כשרת proxy בין אפליקציית האינטרנט והרשת.

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

צריך להוסיף את הקוד הבא אל service-worker.js:

self.addEventListener('fetch', event => {
  console.log('Fetching:', event.request.url);
});

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

יש לבדוק את המסוף ולוודא שלא מתועדים אירועי שליפה. מרעננים את הדף ובודקים שוב את המסוף. הפעם אתם אמורים לראות אירועי אחזור של הדף והנכסים שלו (כמו CSS).

לוחצים על הקישורים דף אחר, דף אחר והקודם.

יוצג לך אירוע אחזור במסוף עבור כל אחד מהדפים והנכסים שלו. האם כל היומנים הגיוניים?

הערה: אם נכנסתם לדף ולא השבתתם את מטמון ה-HTTP, ייתכן שנכסי CSS ו-JavaScript יישמרו במטמון מקומי. במקרה כזה לא יוצגו אירועי אחזור למשאבים האלה.

הסבר

קובץ השירות (service worker) מקבל אירוע אחזור לכל בקשת HTTP שבוצעה על ידי הדפדפן שבו הוא נמצא. האובייקט אחזור אירוע מכיל את הבקשה. האזנה לאירועי אחזור ב-Service worker דומה להאזנה לאירועי קליקים ב-DOM. בקוד שלנו, כשמתרחש אירוע אחזור, אנחנו מתעדים את כתובת ה-URL המבוקשת במסוף (בפועל אנחנו יכולים גם ליצור ולהחזיר תגובה מותאמת אישית משלנו באמצעות משאבים שרירותיים).

למה לא תועדו בשליפה של אירועי אחזור כלשהם ברענון הראשון? כברירת מחדל, אחזור אירועים מדף לא יעבור דרך קובץ שירות (service worker), אלא אם בקשת הדף עצמה עברה דרך קובץ שירות (service worker). כך מובטחת עקביות באתר. אם דף נטען ללא קובץ השירות (service worker), גם משאבי המשנה שלו.

למידע נוסף

קוד הפתרון

כדי לקבל עותק של הקוד הפעיל, צריך להיכנס לתיקייה 04-intercepting-network-requests/.

ל-Service worker יש היקף. ההיקף של קובץ השירות (service worker) קובע באילו נתיבים עובדים (service worker) יורטים בקשות.

מאתרים את ההיקף

יש לעדכן את קוד הרישום בindex.html באמצעות:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('SW registered with scope:', registration.scope);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

מרעננים את הדפדפן. שימו לב שהמסוף מציג את ההיקף של קובץ השירות (במקרה הזה, http://localhost:8081/).

הסבר

ההבטחה שמוחזרת על ידי register() נפתרה לאובייקט הרישום, המכיל את ההיקף של קובץ השירות (service worker)

היקף ברירת המחדל הוא הנתיב אל קובץ ה-service worker, והוא חל על כל הספריות הנמוכות יותר. לכן, קובץ שירות (service worker) בספריית הבסיס של אפליקציה שולט בבקשות מכל הקבצים באפליקציה.

העברת קובץ השירות (service worker)

יש להעביר את service-worker.js לספרייה below/ ולעדכן את כתובת ה-URL של קובץ השירות (service worker) בקוד הרישום index.html.

מבטלים את רישום קובץ ה-worker הנוכחי בדפדפן ומרעננים את הדף.

המסוף מציג שההיקף של קובץ השירות (service worker) הוא עכשיו http://localhost:8081/below/. ב-Chrome, תוכלו גם לראות את היקף קובץ השירות (service worker) בכרטיסיית האפליקציה בכלי הפיתוח:

בדף הראשי, לוחצים על דף אחר, דף אחר והקודם. אילו בקשות אחזור מתעדות? אילו לא?

הסבר

היקף ברירת המחדל של קובץ השירות (service worker) הוא הנתיב אל קובץ ה-service worker. קובץ ה-service worker נמצא עכשיו ב-below/, ולכן הוא ההיקף שלו. כרגע, המסוף מתעד רק אירועי אחזור של another.html, another.css ו-another.js, כי אלה המשאבים היחידים שהם זמינים בהיקף של קובץ השירות (service worker).

הגדרת היקף שרירותי

יש להחזיר את קובץ השירות (service worker) לספריית הבסיס של הפרויקט (app/) ולעדכן את כתובת ה-URL של קובץ השירות (service worker) בקוד הרישום index.html.

יש להשתמש בחומר העזר ב-MDN כדי להגדיר את היקף קובץ השירות (service worker) לספרייה below/ באמצעות הפרמטר האופציונלי ב-register().

מבטלים את הרישום של קובץ השירות (service worker) ומרעננים את הדף. לוחצים על דף אחר, דף אחר והקודם.

שוב המסוף מציג שההיקף של קובץ השירות (service worker) הוא http://localhost:8081/below/ עכשיו, והיומנים מתעדים אירועים רק עבור another.html, another.css ו-another.js.

הסבר

אפשר להגדיר היקף שרירותי על ידי העברת פרמטר נוסף בזמן הרישום, לדוגמה:

navigator.serviceWorker.register('/service-worker.js', {
  scope: '/kitten/'
});

בדוגמה שלמעלה, היקף קובץ השירות (service worker) מוגדר כ-/kitten/. קובץ השירות (service worker) מיירוט בקשות מדפים /kitten/ ו-/kitten/lower/, אבל לא מדפים כמו /kitten או /.

הערה: לא ניתן להגדיר היקף שרירותי שנמצא מעל המיקום בפועל של קובץ השירות (service worker). עם זאת, אם קובץ השירות (service worker) פעיל בלקוח שמציג את הכותרת Service-Worker-Allowed, אפשר לציין היקף מקסימלי של קובץ השירות הזה מעל המיקום של קובץ השירות (service worker).

למידע נוסף

קוד הפתרון

כדי לקבל עותק של הקוד הפעיל, צריך להיכנס לתיקייה solution/.

עכשיו קובץ שירות (service worker) פשוט פועל ותוכלו להבין את מחזור החיים של קובץ השירות (service worker).

למידע נוסף

מחזור חיים של קובץ שירות (service worker)

כדי לראות את כל שיעורי ה-Lab בקורס ההכשרה ל-PWA, יש לעיין ב-Welcome Lab לקורס/