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

איך לחשוב על עובדי שירות.

דייב גדס
דייב גדס

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

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

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

זהה, אבל שונה

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

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

שכבה חדשה

בדרך כלל, בעת בניית אתר עליך לחשוב על שתי שכבות: הלקוח והשרת. ה-Service Worker הוא שכבה חדשה שמופיעה באמצע.

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

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

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

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

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

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

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

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

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

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

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

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

נבחן את הדוגמה הבאה עם מצב גלובלי:

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

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

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

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

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

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