דפוס החשיבה של Service Worker

איך לחשוב על עובדי שירות (service worker).

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

אבל ה-Service Workers לא דומים למה שרובנו מפתחי האינטרנט רגילים אליו. יש להם עקומת למידה תלולה ועוד כמה טעויות שכדאי להיזהר מהן.

לאחרונה Google Developers ואני שיתפתי פעולה על פרויקט — Service Workies — משחק חינמי להבנת פונקציות השירות (service worker). במהלך בנייתו ועבודה עם עובדי השירות המורכבים, נתקלתי בכמה שיבושים. מה שהכי עזר לי היה לחשוב על כמה מטאפורות תיאוריות. בפוסט הזה נחקור את המודלים המנטליים האלה ונקיף את התכונות הפרדוקסליות שהופכות את ה-Service Works גם לבעייתי וגם למדעים.

אותו הדבר אבל שונה

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

אבל התנהגות של Service Worker גורמת לכם לגרד בראש בבלבול. במיוחד כאשר מרעננים את הדף ולא רואים את השינויים בקוד.

שכבה חדשה

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

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

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

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

במשחק Service Workies אנחנו מסבירים בפירוט רב על מחזור החיים של קובץ השירות (service worker) ומשתדלים מאוד לעבוד איתו.

עוצמתי, אבל מוגבל

שימוש בקובץ שירות (service worker) באתר שלך מעניק לך יתרונות מדהימים. האתר שלכם יכול:

  • פועלים בצורה חלקה גם כשהמשתמש במצב אופליין
  • שיפורי ביצועים משמעותיים באמצעות שמירה במטמון
  • להשתמש בהתראות
  • להיות מותקנת בתור PWA

ככל ש-Service Workers יכולים לעשות את זה, הם מוגבלים מבחינתם. הם לא יכולים לבצע פעולות סינכרוניות או באותו שרשור שבו נמצא האתר שלכם. כלומר אין לך גישה אל:

  • localStorage
  • את ה-DOM
  • החלון

החדשות הטובות הן שיש כמה דרכים שבהן הדף שלכם יכול לתקשר עם קובץ השירות שלו, כולל postMessage ישיר, ערוצי הודעות אחד על אחד וערוצי שידור אחד על אחד.

לטווח ארוך, אבל לטווח קצר

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

ב-Service Workies ממחישים את המושג הזה באמצעות Kolohe (עובד שירות ידידותי) שיירוט וטיפול בבקשות.

נעצר

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

waitUntil

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

דוגמה זו מורה לדפדפן שה-Service Worker לא סיים להתקין עד שהמטמון assets נוצר ואכלס תמונה של חרב:

self.addEventListener("install", event => {
  event.waitUntil(
    caches.open("assets").then(cache => {
      return cache.addAll(["/weapons/sword/blade.png"]);
    })
  );
});

היזהרו ממצב גלובלי

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

ניקח לדוגמה את הדוגמה הבאה שמשתמשת במצב גלובלי:

const favoriteNumber = Math.random();
let hasHandledARequest = false;

self.addEventListener("fetch", event => {
  console.log(favoriteNumber);
  console.log(hasHandledARequest);
  hasHandledARequest = true;
});

לכל בקשה ה-service worker ירשום מספר. למשל, 0.13981866382421893. גם המשתנה hasHandledARequest ישתנה ל-true. עכשיו ה-Service Worker לא פעיל קצת, ולכן הדפדפן מפסיק אותו. בפעם הבאה שיש בקשה, קובץ השירות (service worker) נדרש שוב, ולכן הדפדפן יוציא אותו ממצב שינה. הסקריפט שלו נבדק שוב. עכשיו הערך של hasHandledARequest אופס ועכשיו הוא false, והערך favoriteNumber הוא משהו שונה לגמרי — 0.5907281835659033.

אי אפשר להסתמך על מצב מאוחסן ב-Service Worker. בנוסף, יצירת מופעים של דברים כמו Message Channels (ערוצי הודעות) יכולה לגרום לבאגים: תקבלו מכונה חדשה לגמרי בכל פעם שה-Service Worker מפסיק או מתחיל.

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

הדמיה של קובץ שירות (service worker) שהופסק

יחד, אבל בנפרד

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

התעסקות עם מטמון של Service Worker אחר

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

  • SW2 עלול למחוק מטמון ש-SW1 משתמש בו באופן פעיל.
  • SW2 יכול לערוך את התוכן במטמון שבו נעשה שימוש ב-SW1, וכתוצאה מכך SW1 מגיב עם נכסים שהדף לא מצפה להם.

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

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

התחלת פעילות נקייה

יש לוודא שה-Service Workers לא משתמשים זה בזה כדי לא להכפיל אחד את השני. יש דרך אחרת היא לוודא שהם משתמשים בקובצי מטמון שונים. הדרך הקלה ביותר לעשות זאת היא לשנות את שמות המטמון שבהם הם משתמשים.

const version = 1;
const assetCacheName = `assets-${version}`;

self.addEventListener("install", event => {
  caches.open(assetCacheName).then(cache => {
    // confidently do stuff with your very own cache
  });
});

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

תצוגה חזותית של מטמון

סיום הניקיון

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

השיטה caches.match() היא קיצור דרך נפוץ לאחזור פריט מכל מטמון שיש בו התאמה. עם זאת, מתבצע איטרציה במטמון לפי סדר היצירה שלהם. נניח שיש לך שתי גרסאות של קובץ סקריפט app.js בשני מטמוןים שונים – assets-1 ו-assets-2. הדף שלך מצפה לסקריפט החדש יותר שמאוחסן ב-assets-2. אבל אם לא מחקתם את המטמון הישן, caches.match('app.js') יחזיר את הקובץ הישן מ-assets-1 וסביר להניח שהוא יקטע את האתר.

כל מה שצריך כדי לנקות אחרי Service Worker הקודם הוא למחוק כל מטמון שלא צריך ל-Service Worker החדש:

const version = 2;
const assetCacheName = `assets-${version}`;

self.addEventListener("activate", event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== assetCacheName){
            return caches.delete(cacheName);
          }
        });
      );
    });
  );
});

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

דפוס החשיבה של Service Worker

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

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