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

מה צריך להנחות אתכם כשאתם חושבים על Service Worker.

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

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

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

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

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

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

שכבה חדשה

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

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

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

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

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

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

שימוש ב-Service Worker באתר שלכם מספק יתרונות מדהימים. האתר שלכם יכול:

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

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

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

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

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

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

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

נעצר

על אף שעובדי שירות נראה שהם בני נצח, אפשר להפסיק אותם כמעט בכל שלב. הדפדפן לא רוצה לבזבז משאבים על קובץ שירות (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, או שתהיו עצובים בפעם הבאה שהוא יתעורר והמצב שלו יהיה שונה ממה שציפיתם.

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

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. בנוסף, יצירת מופעים של דברים כמו 'ערוצי הודעות' יכולה לגרום לבאגים: תקבלו מופע חדש לגמרי בכל פעם שה-Service Worker יפסיק או יתחיל.

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

תצוגה חזותית של קובץ שירות (service worker) שנעצר

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

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

התעסקות עם מטמון של קובץ שירות אחר

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

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

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

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

התחלת עבודה נקייה

הדרך למנוע מעובדי השירות להקשות אחד על השני היא לוודא שהם משתמשים במטמון שונה. הדרך הקלה ביותר לעשות זאת היא להשתמש בגרסאות של שמות המטמון שבהם הם משתמשים.

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 הקודם כבר מיותר. בשלב זה חשוב לנקות את ה-Service Worker הישן. לא רק זה מכבד את מגבלות על אחסון המטמון, אבל הן יכולות גם למנוע באגים לא מכוונים.

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

כל מה שצריך כדי לנקות אחרי שעובדי השירות הקודמים צריכים למחוק את המטמון שלא נדרש ל-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 כדי להביס את המפלצות בעולם האמיתי.