ה-codelab הזה הוא חלק מקורס ההדרכה בנושא פיתוח Progressive Web Apps, שפותח על ידי צוות ההדרכה של Google Developers. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר.
פרטים מלאים על הקורס זמינים במאמר סקירה כללית על פיתוח Progressive Web Apps.
מבוא
בשיעור ה-Lab הזה נסביר איך ליצור service worker פשוט ומהו מחזור החיים של service worker.
מה תלמדו
- יצירה של סקריפט בסיסי של Service Worker, התקנה שלו וביצוע ניפוי באגים פשוט
מה חשוב לדעת
- JavaScript ו-HTML בסיסיים
- מושגים ותחביר בסיסי של Promises ב-ES2015
- איך מפעילים את Developer Console
מה צריך לדעת לפני שמתחילים
- מחשב עם גישה לטרמינל או למעטפת
- חיבור לאינטרנט
- דפדפן שתומך ב-Service Workers
- כלי לעריכת טקסט
מורידים או משכפלים את מאגר 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) ולנקות את כל המטמונים של קובצי השירות ב-localhost כדי שלא יפריעו לשיעור ה-Lab. כדי לעשות את זה בכלי הפיתוח ל-Chrome, לוחצים על ניקוי נתוני האתר בקטע ניקוי האחסון שבכרטיסייה אפליקציה.
פותחים את התיקייה service-worker-lab/app/ בכלי לעריכת טקסט. בתיקייה app/ תבנו את המעבדה.
התיקייה הזו מכילה:
-
below/another.html,js/another.js,js/other.jsו-other.htmlהם משאבים לדוגמה שבהם אנחנו משתמשים כדי להתנסות בהיקף של Service Worker - התיקייה
styles/מכילה את קובצי ה-CSS של שיעור ה-Lab הזה - תיקיית
test/מכילה קבצים לבדיקת ההתקדמות -
index.htmlהוא דף ה-HTML הראשי של האתר או האפליקציה לדוגמה -
service-worker.jsהוא קובץ JavaScript שמשמש ליצירת Service Worker -
package.jsonו-package-lock.jsonעוקבים אחרי חבילות הצמתים שמשמשות בפרויקט הזה -
server.jsהוא שרת פשוט של Express שבו אנחנו משתמשים כדי לארח את האפליקציה שלנו
פותחים את 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), לחיצה על הכרטיסייה Application ואז לחיצה על האפשרות Service Workers. אמורה להופיע כותרת שדומה לכותרת הבאה:

אופציונלי: פותחים את האתר בדפדפן שלא נתמך ומוודאים שהתנאי של בדיקת התמיכה פועל.
הסבר
הקוד שלמעלה רושם את הקובץ service-worker.js כ-service worker. קודם כל, המדיניות בודקת אם הדפדפן תומך ב-service workers. צריך לעשות את זה בכל פעם שרושמים קובץ שירות (service worker), כי יכול להיות שדפדפנים מסוימים לא תומכים בקובצי שירות. לאחר מכן, הקוד רושם את ה-service worker באמצעות השיטה register של ServiceWorkerContainer API, שנמצא בממשק Navigator של החלון.
navigator.serviceWorker.register(...) מחזירה הבטחה שמושלמת עם אובייקט registration אחרי שרשמנו את ה-service worker בהצלחה. אם הרישום נכשל, ההבטחה תידחה.
שינויים בסטטוס של Service Worker מפעילים אירועים ב-Service Worker.
הוספת פונקציות event listener
פותחים את service-worker.js בכלי לעריכת טקסט.
מוסיפים את פונקציות ה-event listener הבאות ל-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 מתרחשים בסדר מוגדר בתוך ה-Service Worker, ותמיד צריכים להופיע בסדר הצפוי.
הסבר
סוף ההרשמה מסומן על ידי פליטת אירוע install על ידי ה-service worker. בדוגמת הקוד שלמעלה, הודעה נרשמת ביומן בתוך install event listener, אבל באפליקציה אמיתית זה יהיה מקום טוב לשמירת נכסים סטטיים במטמון.
כשקובץ שירות (service worker) רשום, הדפדפן מזהה אם קובץ השירות חדש (או כי הוא שונה מקובץ השירות שהותקן קודם או כי אין קובץ שירות רשום לאתר הזה). אם ה-service worker חדש (כמו במקרה הזה), הדפדפן מתקין אותו.
קובץ השירות (service worker) פולט אירוע activate כשהוא מקבל שליטה בדף. הקוד שלמעלה מתעד הודעה כאן, אבל האירוע הזה משמש לעיתים קרובות לעדכון מטמונים.
יכול להיות פעיל רק Service Worker אחד בכל פעם עבור היקף נתון (ראו הסבר על היקף של Service 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 בכלי הפיתוח.

סוגרים את כל הדפים שמשויכים ל-service worker. לאחר מכן, פותחים מחדש את localhost:8081/. יומן המסוף אמור לציין שעובד השירות החדש הופעל עכשיו.
הערה: אם אתם מקבלים תוצאות לא צפויות, ודאו שהמטמון של HTTP מושבת בכלי הפיתוח.
הסבר
הדפדפן מזהה הבדל בבייט בין קובץ ה-service worker החדש לבין הקובץ הקיים (בגלל התגובה שנוספה), ולכן ה-service worker החדש מותקן. מכיוון שיכול להיות פעיל רק service worker אחד בכל פעם (במסגרת היקף נתון), גם אם ה-service worker החדש מותקן, הוא לא מופעל עד שה-service worker הקיים לא נמצא יותר בשימוש. סגירת כל הדפים שנמצאים בשליטת קובץ השירות הישן מאפשרת לנו להפעיל את קובץ השירות החדש.
דילוג על שלב ההמתנה
אפשר להפעיל מיד Service Worker חדש, גם אם קיים Service Worker, על ידי דילוג על שלב ההמתנה.
ב-service-worker.js, מוסיפים קריאה ל-skipWaiting ב-event listener של install:
self.skipWaiting();שומרים את הקובץ ומרעננים את הדף. שימו לב שקובץ השירות החדש מותקן ומופעל באופן מיידי, גם אם קובץ שירות קודם היה בשליטה.
הסבר
השיטה skipWaiting() מאפשרת להפעיל Service Worker ברגע שההתקנה מסתיימת. הפונקציה skipWaiting() בדרך כלל ממוקמת ב-event listener של ההתקנה, אבל אפשר להפעיל אותה בכל מקום במהלך שלב ההמתנה או לפניו. במאמר הזה מוסבר מתי ואיך להשתמש ב-skipWaiting(). מעכשיו, בשאר המעבדה, נוכל לבדוק קוד חדש של service worker בלי לבטל את הרישום של ה-service worker באופן ידני.
אפשר לקבל מידע נוסף
קובצי שירות (service workers) יכולים לשמש כפרוקסי בין אפליקציית האינטרנט לבין הרשת.
נוסיף מאזין אחזור כדי ליירט בקשות מהדומיין שלנו.
מוסיפים את הקוד הבא ל-service-worker.js:
self.addEventListener('fetch', event => {
console.log('Fetching:', event.request.url);
});שומרים את הסקריפט ומרעננים את הדף כדי להתקין ולהפעיל את ה-service worker המעודכן.
בודקים את המסוף ורואים שלא נרשמו אירועים של אחזור. מרעננים את הדף ובודקים שוב את המסוף. הפעם אמורים להופיע אירועי אחזור של הדף והנכסים שלו (כמו CSS).
לוחצים על הקישורים דף אחר, עוד דף וחזרה.
במסוף יוצגו אירועי אחזור לכל אחד מהדפים ולנכסים שלהם. האם כל היומנים הגיוניים?
הערה: אם אתם מבקרים בדף ומטמון ה-HTTP לא מושבת, יכול להיות שנכסי CSS ו-JavaScript יישמרו במטמון באופן מקומי. אם זה קורה, לא תראו אירועי אחזור של המשאבים האלה.
הסבר
כל בקשת HTTP שהדפדפן שולח במסגרת ההיקף של ה-service worker, גורמת ל-service worker לקבל אירוע fetch. אובייקט fetch event מכיל את הבקשה. האזנה לאירועי אחזור ב-service worker דומה להאזנה לאירועי קליק ב-DOM. בקוד שלנו, כשמתרחש אירוע אחזור, אנחנו מתעדים את כתובת ה-URL המבוקשת במסוף (בפועל, יכולנו גם ליצור ולהחזיר תגובה מותאמת אישית עם משאבים שרירותיים).
למה לא נרשמו אירועי אחזור ברענון הראשון? כברירת מחדל, אירועי אחזור מדף לא עוברים דרך קובץ שירות (service worker) אלא אם הבקשה לדף עצמו עברה דרך קובץ שירות. כך מובטחת עקביות באתר: אם דף נטען בלי קובץ השירות, גם משאבי המשנה שלו נטענים בלי קובץ השירות.
אפשר לקבל מידע נוסף
קוד הפתרון
כדי לקבל עותק של הקוד הפעיל, עוברים לתיקייה 04-intercepting-network-requests/.
לקובצי שירות (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.
מבטלים את הרישום של קובץ השירות הנוכחי בדפדפן ומרעננים את הדף.
במסוף מוצג שההיקף של Service Worker הוא עכשיו http://localhost:8081/below/. ב-Chrome, אפשר גם לראות את היקף ה-service worker בכרטיסיית האפליקציה בכלי הפיתוח:

חוזרים לדף הראשי ולוחצים על דף אחר, דף נוסף וחזרה. אילו בקשות אחזור נרשמות ביומן? ואילו לא?
הסבר
ההיקף שמוגדר כברירת מחדל ל-service worker הוא הנתיב לקובץ ה-service worker. קובץ ה-Service Worker נמצא עכשיו ב-below/, ולכן זהו ההיקף שלו. המסוף מתעד עכשיו רק אירועי אחזור של another.html, another.css ו-another.js, כי אלה המשאבים היחידים שנמצאים בטווח של קובץ השירות.
הגדרת היקף שרירותי
מעבירים את קובץ ה-service worker בחזרה לתיקיית השורש של הפרויקט (app/) ומעדכנים את כתובת ה-URL של קובץ ה-service worker בקוד הרישום ב-index.html.
כדי להגדיר את היקף השירות של ה-Service Worker לספרייה below/ באמצעות הפרמטר האופציונלי ב-register(), אפשר להיעזר בהפניה ב-MDN.
מבטלים את הרישום של קובץ השירות (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 הזה מעל המיקום של ה-service worker.
אפשר לקבל מידע נוסף
קוד הפתרון
כדי לקבל עותק של הקוד הפעיל, עוברים לתיקייה solution/.
עכשיו יש לכם service worker פשוט שפועל, ואתם מבינים את מחזור החיים של service worker.
אפשר לקבל מידע נוסף
כדי לראות את כל ה-codelabs בקורס ההדרכה בנושא PWA, אפשר לעיין ב-codelab המבוא לקורס.