• בלוג
  • מדריך מקוצר ל Service Worker

מדריך מקוצר ל Service Worker

פוסט זה כולל טיפ קצר בנושא פיתוח Front End. אם אתם רוצים ללמוד יותר לעומק על פיתוח Front End מהבסיס ועד הנושאים המתקדמים תשמחו לשמוע שבניתי קורס וידאו מקיף בנושא זה הכולל מעל 50 שיעורי וידאו והמון תרגול מעשי.
למידע נוסף והצטרפות לקורס בקרו בדף קורס Front End באתר.
 

סרביס וורקר הוא אחד הממשקים המורכבים של פיתוח Web אבל גם כלי שמאפשר התנהגויות מורכבות שלא ראינו לפני ויכול לעזור לנו להציע חווית שימוש טובה יותר למשתמשים שלנו.

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

1. מי צריך Service Worker

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

  1. ממשק Push API מספק דרך לשלוח לגולשים הודעת Push גם כשהדפדפן שלהם כבוי.

  2. ממשק Cache API מאפשר לשמור קבצים בתוך הדפדפן כדי שקבצים אלה יהיו זמינים גם במצב Offline, כלומר כשהגולש לא מחובר לרשת.

  3. ממשק Background Sync מאפשר לתזמן פעולה כך שהיא לא תפסיק באמצע אם חיבור האינטרנט נקטע (או אם משתמש עזב את האתר).

סרביס וורקר הוא מעין מיני תוסף לדפדפן שרץ כל הזמן ברקע ומאזין לאירועים. עבור Push API למשל ה Service Worker יאזין לאירוע שנקרא push. עבור Cache API הוא יאזין לאירוע שנקרא fetch ועבור Background Sync הוא יאזין לאירוע שנקרא sync. בגלל שה Service Worker רץ כל הזמן ברקע הוא יוכל לטפל באירועים אלה גם אם האתר שלכם לא פתוח בדפדפן.

2. ה Service Worker הראשון שלי

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

קוד JavaScript רגיל ישתמש בפקודה הבאה כדי לבקש מהדפדפן להוריד ולהתקין את ה Service Worker שכתבתם:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

הקוד המודבק מחפש קובץ בשם sw.js ובו צריך להיות ה Service Worker. הדפדפן יוריד את הקובץ ויתקין את הוורקר.

דוגמא קצרה לקוד ה Service Worker (כלומר לתוכן של הקובץ sw.js) היא:

self.addEventListener('install', function(event) {
  console.log('Install');
});

self.addEventListener('activate', function(event) {
  console.log('Activate');
});

כעת בפרויקט שלי יש קובץ index.html שטוען קובץ main.js, ובקובץ main.js יש את הפקודה שטוענת את הקובץ sw.js. עם שלושת הקבצים האלה אפשר להריץ את הפרויקט ואז תראו בדפדפן את ההודעות מה Service Worker על התקנה והפעלה של הפועל.

אירוע install של Service Worker מתרחש כשמורידים קובץ sw.js חדש (וכן ברור שאפשר לקרוא לו בכל שם). אחרי שהדפדפן סיים להתקין את ה Service Worker אם אין אף גירסא קודמת של הפועל שכרגע רצה יישלח גם אירוע activate. לעומת זאת אם יש דף כלשהו שפתוח ורץ עם הגירסא הקודמת של ה Service Worker אז הדפדפן לא יקרא ל activate עד שהדף האחרון שמתקשר עם הגירסא הקודמת ייסגר.

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

3. טיפול באירוע Fetch של Service Worker

השימוש הראשון והנפוץ ביותר ל Service Worker יהיה לאפשר למשתמשים לשמור חלק מהאתר אצלם ב Cache כך שהאתר יהיה זמין לצפיה גם כשאין חיבור רשת. הבעיה עם Service Worker היא ש API זה לבד לא עושה כלום, והוא חייב להיעזר ב APIs נוספים בשביל מימוש הפונקציונאליות.

בדוגמא של Offline Content אנחנו ניעזר ב Cache API. נכתוב את הפונקציה הבאה בתוך הקובץ sw.js:

function precache() {
  console.log("Prepare cache");
  return caches.open(CACHE).then(function (cache) {
    return cache.addAll([
      '/',
      './index.html',
      './game.css',
      './game.js',
    ]);
  });
}

מלבד העובדה שהיא כתובה ב sw.js אין לפונקציה שום קשר לממשק של Service Worker. היא משתמשת ב API אחר שנקרא Cache API. ממשק זה מאפשר לבקש מהדפדפן לשמור ב Cache נכסים לפי הכתובות שלהם, ובמקרה שלנו אני שומר את הנתיב הראשי, את index.html ואת קבצי ה js ו css של אתר מסוים.

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

נוסיף את המימוש הבא להאזנה לאירוע fetch יחד עם הפונקציות fromCache ו fromNetwork:


self.addEventListener('fetch', function(evt) {
  console.log('The service worker is serving the asset');
  console.log(evt.request)

  evt.respondWith(fromNetwork(evt.request, 400).catch(function () {
    return fromCache(evt.request);
  }));  
});

function fromNetwork(request, timeout) {
  return new Promise(function (fulfill, reject) {
    var timeoutId = setTimeout(reject, timeout);
    fetch(request).then(function (response) {
      clearTimeout(timeoutId);
      fulfill(response);
    }, reject);
  });
}

function fromCache(request) {
  console.log('Searching in cache for ', request);
  return caches.open(CACHE).then(function (cache) {
    console.log('Cache open: ', cache);
    return cache.match(request).then(function (matching) {
      console.log('matching = ', matching);
      return matching || Promise.reject('no-match');
    });
  });
}

לסיום נוסיף קריאה ל precache לקוד הטיפול באירוע install:

self.addEventListener('install', function(evt) {
  console.log('Install');
  evt.waitUntil(precache());
});

הקוד הזה יגרום לאתר שלנו להיות זמין גם במצב Offline:

  1. ה Service Worker יוריד את כל הנכסים החשובים וישמור אותם ב Cache מיד אחרי שהותקן.

  2. בפעם הבאה שגולש ינסה לגלוש לאתר, הדפדפן יפנה קודם כל ל Service Worker דרך אירוע fetch.

  3. ה Service Worker ינסה להביא את המידע מהרשת, אבל אם הגולש לא מחובר לרשת הוא ישלוף את העותק האחרון שיש לו מה Cache.

מי שרוצה לראות את הקוד בפעולה יוכל להוריד את הפרויקט המלא מגיטהאב בקישור:

קוד לדוגמת Offline Site באמצעות Service Worker.

4. הקפצת Notifications מתוך Service Worker

ממשק תכנותי נוסף ש Service Worker מספק גישה אליו הוא ה Notifications API. ממשק זה מאפשר לאתר ווב לשלוח הודעות קופצות למשתמשים בעקבות אירועים שונים, כאשר האירוע שבדרך כלל נרצה להשתמש בו הוא כמובן אירוע push (אותו נקבל מ API אחר שנקרא Push API).

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

הפונקציה הבאה בתוך הקובץ main.js תשלח Notification למשתמש בעקבות לחיצה על כפתור:

function showNotification() {
  Notification.requestPermission(function(result) {
    if (result === 'granted') {
      navigator.serviceWorker.ready.then(function(registration) {
        registration.showNotification('Vibration Sample', {
          body: 'Buzz! Buzz!',
          vibrate: [200, 100, 200, 100, 200, 100, 200],
          tag: 'vibration-sample'
        });
      });
    }
  });
}

כמה דברים חשובים לגבי הקוד:

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

  2. אחרי שקיבלנו הרשאה הפונקציה משתמשת בפקודה navigator.serviceWorker.ready כדי לקבל אוביקט שנקרא registration, ואחרי זה מתוך אוביקט זה מפעילה את הפונקציה showNotification. בשביל שכל העסק הזה יעבוד חייבים שיהיה Service Worker פעיל על העמוד.

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

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

self.addEventListener('push', function(event) {
  const payload = event.data ? event.data.text() : 'no payload';
  event.waitUntil(
    self.registration.showNotification('ServiceWorker Cookbook', {
      body: payload,
    })
  );
});

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

https://serviceworke.rs/push-payload.html

והקוד עצמו בפעולה נמצא בקישור כאן: https://serviceworke.rs/push-payload/.

5. לאן ממשיכים

אחרי שכתבתם מספר Service Workers שווה להמשיך ולעבור על כל המתכונים באתר https://serviceworke.rs/. אתם תמצאו שם דוגמאות קוד להמון סוגים של Service Workers, כולל כאלה ששולחים נוטיפיקציות, שומרים תוכן לצפיה Offline ואפילו מימוש של Load Balancer בצד הלקוח.

הסרביס וורקר הוא אחד ה APIs הכי מגוונים שיש היום בעולם ה Web. הוא מאוד ממוקד למשימות ספציפיות וזו אולי הסיבה שמתכנתים רבים לא מכירים ממשק זה, אבל אחרי שמתגברים על כתיבת הוורקרים הראשונים גם אתם תראו שהשד לא כל כך נורא ואפילו נחמד לבנות פונקציונאליות Offline באמצעותו.