קשה לדעת מה עושים שירותי העבודה בלי להבין את מחזור החיים שלהם. התהליכים הפנימיים שלהם ייראו מעורפלים, ואולי אף שרירותיים. חשוב לזכור שהתנהגויות של שירותי עובדים מוגדרות ומפורטות היטב, כמו כל ממשק API אחר בדפדפן, ומאפשרות להשתמש באפליקציות אופליין, וגם מאפשרות לבצע עדכונים בלי להפריע לחוויית המשתמש.
לפני שנכנסים לעומק של Workbox, חשוב להבין את מחזור החיים של עובד השירות כדי להבין את הפעולות של Workbox.
הגדרת מונחים
לפני שנמשיך לדבר על מחזור החיים של עובד השירות, כדאי להגדיר כמה מונחים שקשורים לאופן שבו מחזור החיים הזה פועל.
שליטה והיקף
הרעיון של שליטה הוא קריטי להבנת האופן שבו עובדי שירות פועלים. דף שמתואר כמנוהל על ידי קובץ שירות הוא דף שמאפשר לקובץ שירות ליירט בקשות רשת בשם הדף. קובץ השירות נמצא וגם יכול לבצע משימות עבור הדף בטווח נתון.
היקף
היקף של עובד שירות נקבע לפי המיקום שלו בשרת אינטרנט.
אם קובץ שירות פועל בדף שנמצא בכתובת /subdir/index.html
, והוא נמצא בכתובת /subdir/sw.js
, היקף קובץ השירות הוא /subdir/
.
כדי לראות את המושג 'היקף' בפעולה, כדאי לעיין בדוגמה הבאה:
- עוברים לכתובת https://service-worker-scope-viewer.glitch.me/subdir/index.html.
תופיע הודעה על כך שאין קובץ שירות ששולט בדף.
עם זאת, הדף הזה רושם קובץ שירות (service worker) מ-
https://service-worker-scope-viewer.glitch.me/subdir/sw.js
. - טוענים מחדש את הדף. קובץ השירות רשום ועכשיו פעיל, ולכן הוא שולט בדף. יוצג טופס שמכיל את ההיקף, המצב הנוכחי וכתובת ה-URL של ה-service worker. הערה: הצורך לטעון מחדש את הדף לא קשור להיקף, אלא למחזור החיים של קובץ השירות (service worker), שבו נסביר בהמשך.
- עכשיו עוברים לכתובת https://service-worker-scope-viewer.glitch.me/index.html. למרות שקובץ שירות (service worker) נרשם במקור הזה, עדיין מופיעה ההודעה שאין קובץ שירות (service worker) פעיל. הסיבה לכך היא שהדף הזה לא נמצא בטווח של קובץ השירות (service worker) הרשום.
היקף ההגדרה מגביל את הדפים שנשלטים על ידי קובץ השירות.
בדוגמה הזו, המשמעות היא שקובץ ה-service worker שנטען מ-/subdir/sw.js
יכול לשלוט רק בדפים שנמצאים ב-/subdir/
או ב-subtree שלו.
כך פועל תיחום ההיקף כברירת מחדל, אבל אפשר לשנות את ההיקף המקסימלי המותרת על ידי הגדרת כותרת התגובה Service-Worker-Allowed
, וגם על ידי העברת אפשרות scope
לשיטה register
.
אלא אם יש סיבה טובה מאוד להגביל את היקף קובץ השירות (service worker) לקבוצת משנה של מקור, כדאי לטעון קובץ שירות מהספרייה ברמה הבסיסית (root) של שרת האינטרנט כדי שהיקף הפעילות שלו יהיה רחב ככל האפשר, ולא לדאוג לגבי הכותרת Service-Worker-Allowed
. כך קל יותר לכולם.
לקוח
כשאומרים שקובץ שירות שולט בדף, הכוונה היא שהוא שולט בלקוח.
לקוח הוא כל דף פתוח שכתובת ה-URL שלו נכללת בהיקף של אותו עובד שירות.
באופן ספציפי, אלה הן מופעים של WindowClient
.
מחזור החיים של עובד שירות חדש
כדי שעובד שירות יוכל לשלוט בדף, צריך קודם ליצור אותו. נתחיל בתיאור מה קורה כשפורסים שירות חדש לגמרי לאתר ללא שירות פעיל.
הרשמה
הרישום הוא השלב הראשון במחזור החיים של קובץ השירות:
<!-- In index.html, for example: -->
<script>
// Don't register the service worker
// until the page has fully loaded
window.addEventListener('load', () => {
// Is service worker available?
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service worker registered!');
}).catch((error) => {
console.warn('Error registering service worker:');
console.warn(error);
});
}
});
</script>
הקוד הזה פועל ב-thread הראשי ומבצע את הפעולות הבאות:
- מכיוון שהביקור הראשון של המשתמש באתר מתרחש ללא עובד שירות רשום, צריך להמתין עד שהדף נטען במלואו לפני שמירצים עובד שירות. כך אפשר למנוע תחרות על רוחב הפס אם שירות ה-worker מאחסן משהו במטמון מראש.
- למרות שיש תמיכה רחבה בקובצי שירות, כדאי לבצע בדיקה מהירה כדי למנוע שגיאות בדפדפנים שבהם אין תמיכה בהם.
- כשהדף נטען במלואו, ואם יש תמיכה בקובצי שירות, רושמים את
/sw.js
.
חשוב להבין כמה דברים:
- רק דרך HTTPS או localhost.
- אם התוכן של ה-service worker מכיל שגיאות תחביר, הרישום נכשל וה-service worker נמחק.
- תזכורת: קובצי שירות פועלים בתוך היקף מסוים. כאן, ההיקף הוא המקור כולו, כפי שהוא נטען מספריית השורש.
- כשהרישום מתחיל, המצב של ה-service worker מוגדר ל-
'installing'
.
בסיום הרישום, תתחיל ההתקנה.
התקנה
אירוע install
של שירות העבודה מופעל אחרי הרישום.
השיטה install
נקראת רק פעם אחת לכל עובד שירות, והיא לא תופעל שוב עד לעדכון שלה.
אפשר לרשום קריאה חוזרת (callback) לאירוע install
בהיקף של העובד באמצעות addEventListener
:
// /sw.js
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v1';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v1'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.bc7b80b7.css',
'/css/home.fe5d0b23.css',
'/js/home.d3cc4ba4.js',
'/js/jquery.43ca4933.js'
]);
}));
});
הפעולה הזו יוצרת מכונה חדשה של Cache
ומאחסנת נכסים במטמון מראש.
בהמשך נדבר בהרחבה על אחסון נתונים במטמון מראש, אבל עכשיו נתמקד בתפקיד של event.waitUntil
. event.waitUntil
מקבלת הבטחה ומחכה עד שההבטחה תיפתר.
בדוגמה הזו, ההבטחה מבצעת שני דברים אסינכרונים:
- יצירת מכונה חדשה של
Cache
בשם'MyFancyCache_v1'
. - אחרי יצירת המטמון, מערך של כתובות URL של נכסים מאוחסן במטמון מראש באמצעות השיטה האסינכרונית
addAll
.
ההתקנה נכשלת אם הבטחות שהועברו ל-event.waitUntil
נדחו.
במקרה כזה, ה-service worker מושלך.
אם הבטחות ה-Promise יתקבלו, ההתקנה תתבצע בהצלחה והסטטוס של ה-service worker ישתנה ל-'installed'
, ולאחר מכן הוא יופעל.
Activation (הפעלה)
אם הרישום וההתקנה מסתיימים בהצלחה, ה-service worker מופעל והמצב שלו הופך ל-'activating'
. אפשר לבצע משימות במהלך ההפעלה באירוע activate
של ה-service worker.
משימה אופיינית באירוע הזה היא לנקות מטמון ישן, אבל בשלב הזה זה לא רלוונטי ל-service worker חדש לגמרי, ונרחיב על כך בהמשך כשנדבר על עדכוני service worker.
בשירותי עובדים חדשים, האירוע activate
מופעל מיד אחרי שהאירוע install
מסתיים בהצלחה.
בסיום ההפעלה, הסטטוס של ה-service worker הופך ל-'activated'
.
חשוב לזכור שלפי ברירת המחדל, קובץ השירות החדש לא יתחיל לשלוט בדף עד לניווט הבא או לרענון הדף.
טיפול בעדכונים של שירותי העבודה
אחרי הפריסה של ה-service worker הראשון, סביר להניח שתצטרכו לעדכן אותו מאוחר יותר. לדוגמה, יכול להיות שיהיה צורך בעדכון אם יהיו שינויים בטיפול בבקשות או בלוגיקה של האחסון המוקדם.
מתי מתבצעים עדכונים
הדפדפנים יבדקו אם יש עדכונים ל-service worker במקרים הבאים:
- המשתמש עובר לדף בטווח של קובץ השירות.
navigator.serviceWorker.register()
נקראת עם כתובת URL שונה מכתובת ה-URL של קובץ השירות (service worker) שמותקן כרגע – אבל אל תשנו את כתובת ה-URL של קובץ השירות!navigator.serviceWorker.register()
נקראת עם אותה כתובת URL של ה-service worker שהותקן, אבל בהיקף שונה. שוב, כדי להימנע מכך, אם אפשר, כדאי לשמור את ההיקף ברמה הבסיסית של המקור.- כשאירועים כמו
'push'
או'sync'
הופעלו ב-24 השעות האחרונות – אבל אל דאגה, עדיין לא צריך להתייחס לאירועים האלה.
איך מתבצעים העדכונים
חשוב לדעת מתי הדפדפן מעדכן את ה-service worker, אבל חשוב גם לדעת איך הוא עושה זאת. בהנחה שכתובת ה-URL או ההיקף של עובד השירות לא השתנו, עובד שירות שמותקן כרגע מתעדכן לגרסה חדשה רק אם התוכן שלו השתנה.
דפדפנים מזהים שינויים בכמה דרכים:
- שינויים בייט אחרי בייט בסקריפטים שביקשו
importScripts
, אם רלוונטי. - שינויים בקוד ברמה העליונה של ה-service worker, שמשפיעים על טביעת האצבע שהדפדפן יצר ממנו.
הדפדפן עושה כאן הרבה עבודה קשה. כדי לוודא שלדפדפן יש את כל מה שהוא צריך כדי לזהות בצורה מהימנה שינויים בתוכן של שירות ה-worker, אל תבקשו מהמטמון של HTTP לשמור אותו ואל תשנו את שם הקובץ שלו. הדפדפן מבצע בדיקות עדכונים באופן אוטומטי כשמתבצע ניווט לדף חדש בהיקף של עובד שירות.
הפעלה ידנית של בדיקות עדכונים
בנוגע לעדכונים, בדרך כלל לא צריך לשנות את הלוגיקה של הרישום. עם זאת, יכול להיות שיהיה חריג אחד, אם הסשנים באתר נמשכים זמן רב. מצב כזה יכול לקרות באפליקציות של דף יחיד שבהן בקשות ניווט נדירות, כי בדרך כלל האפליקציה נתקלת בבקשת ניווט אחת בתחילת מחזור החיים שלה. במקרים כאלה, אפשר להפעיל עדכון ידני בשרשור הראשי:
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
באתרים רגילים, או בכל מקרה שבו סשנים של משתמשים לא נמשכים זמן רב, כנראה שאין צורך להפעיל עדכונים ידניים.
התקנה
כשמשתמשים ב-bundler כדי ליצור נכסים סטטיים, השמות של הנכסים האלה יכללו גיבוב (hash), למשל framework.3defa9d2.js
.
נניח שחלק מהנכסים האלה מאוחסנים במטמון מראש כדי לאפשר גישה אופליין בהמשך.
לשם כך, צריך לעדכן את ה-service worker כדי לאחסן מראש נכסים מעודכנים:
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v2';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v2'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.ced4aef2.css',
'/css/home.cbe409ad.css',
'/js/home.109defa4.js',
'/js/jquery.38caf32d.js'
]);
}));
});
יש שני הבדלים מהדוגמה הראשונה של אירוע install
שצוינה למעלה:
- נוצרת מכונה חדשה של
Cache
עם מפתח של'MyFancyCacheName_v2'
. - שמות הנכסים ששמורים במטמון השתנו.
חשוב לזכור שקובץ השירות המעודכן מותקן לצד קובץ השירות הקודם. המשמעות היא ש-service worker הישן עדיין שולט בדפים הפתוחים, ו-service worker החדש נכנס למצב המתנה אחרי ההתקנה עד שהוא מופעל.
כברירת מחדל, עובד שירות חדש יופעל כשלא יהיו לקוחות בשליטת העובד הישן. המצב הזה מתרחש כשכל הכרטיסיות הפתוחות באתר הרלוונטי נסגרות.
Activation (הפעלה)
כשקובץ השירות המעודכן מותקן ושלב ההמתנה מסתיים, הוא מופעל וקובץ השירות הישן מבוטל.
משימה נפוצה לביצוע באירוע activate
של קובץ שירות (service worker) מעודכן היא לנקות מטמון ישן.
כדי להסיר מטמון ישן, מקבלים את המפתחות לכל המופעים הפתוחים של Cache
באמצעות caches.keys
ומוחקים מטמון שלא נמצא ברשימת ההיתרים המוגדרת באמצעות caches.delete
:
self.addEventListener('activate', (event) => {
// Specify allowed cache keys
const cacheAllowList = ['MyFancyCacheName_v2'];
// Get all the currently active `Cache` instances.
event.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});
מטמון ישן לא מתנקה מעצמו.
אנחנו צריכים לעשות זאת בעצמנו, אחרת אנחנו עלולים לחרוג ממכסות האחסון.
מכיוון ש-'MyFancyCacheName_v1'
מה-service worker הראשון לא עדכני, רשימת ההיתרים של המטמון מתעדכנת כך שתכלול את 'MyFancyCacheName_v2'
, שמוחקת מטמון עם שם אחר.
האירוע activate
יסתיים אחרי שהמטמון הישן יוסר.
בשלב הזה, קובץ ה-service worker החדש יקבל שליטה בדף ויחליף את קובץ ה-service worker הישן.
מחזור החיים נמשך
בין שאתם משתמשים ב-Workbox כדי לטפל בפריסה ובעדכונים של שירותי ה-Worker, ובין שאתם משתמשים ישירות ב-Service Worker API, כדאי להבין את מחזור החיים של שירותי ה-Worker. אחרי שמבינים את זה, ההתנהגויות של שירותי העבודה צריכות להיראות הגיוניות יותר מאשר מסתוריות.
אם אתם רוצים לקבל מידע מעמיק יותר בנושא הזה, כדאי לעיין במאמר הזה של ג'ייק ארקדיל (Jake Archibald). יש המון ניואנסים בתהליך כולו של מחזור החיים של השירות, אבל אפשר ללמוד אותם, והידע הזה יעזור לכם מאוד כשאתם משתמשים ב-Workbox.