טיפול מיידי בעדכונים של קובצי השירות

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

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

הקוד שצריך להוסיף לדף

הקוד הבא פועל ברכיב <script> מוטבע, באמצעות מודולים של JavaScript שיובאו מגרסה של workbox-window שמתארחת ב-CDN. מתבצע רישום של קובץ שירות (service worker) באמצעות workbox-window, ויגיב אם ה-Service Worker נתקע בשלב ההמתנה. כשנמצא עובד שירות בהמתנה, הקוד הזה מודיע למשתמש שיש גרסה מעודכנת של האתר ומבקש ממנו לטעון מחדש.

<!-- This script tag uses JavaScript modules, so the proper `type` attribute value is required -->
<script type="module">
  // This code sample uses features introduced in Workbox v6.
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');
    let registration;

    const showSkipWaitingPrompt = async (event) => {
      // Assuming the user accepted the update, set up a listener
      // that will reload the page as soon as the previously waiting
      // service worker has taken control.
      wb.addEventListener('controlling', () => {
        // At this point, reloading will ensure that the current
        // tab is loaded under the control of the new service worker.
        // Depending on your web app, you may want to auto-save or
        // persist transient state before triggering the reload.
        window.location.reload();
      });

      // When `event.wasWaitingBeforeRegister` is true, a previously
      // updated service worker is still waiting.
      // You may want to customize the UI prompt accordingly.

      // This code assumes your app has a promptForUpdate() method,
      // which returns true if the user wants to update.
      // Implementing this is app-specific; some examples are:
      // https://open-ui.org/components/alert.research or
      // https://open-ui.org/components/toast.research
      const updateAccepted = await promptForUpdate();

      if (updateAccepted) {
        wb.messageSkipWaiting();
      }
    };

    // Add an event listener to detect when the registered
    // service worker has installed but is waiting to activate.
    wb.addEventListener('waiting', (event) => {
      showSkipWaitingPrompt(event);
    });

    wb.register();
  }
</script>

אם הוא יאשר את הבקשה, messageSkipWaiting() ינחה לעובדי שירות ההמתנה להפעיל את self.skipWaiting(), כלומר הוא יתחיל לפעול. לאחר ההפעלה, ה-Service Worker החדש ישתלט על כל הלקוחות הקיימים, ויפעיל את האירוע controlling ב-workbox-window. במקרה כזה, הדף הנוכחי נטען מחדש באמצעות הגרסה האחרונה של כל הנכסים שנשמרו מראש במטמון וכל לוגיקת ניתוב חדשה שנמצאת ב-Service Worker המעודכן.

הקוד שיש להזין ב-Service Worker

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

אם אתם כותבים קובץ שירות (service worker) משלכם – אולי בשילוב עם אחד מכלי ה-build של Workbox במצב injectManifest – תצטרכו להוסיף את הקוד הבא בעצמכם:

addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

הפעולה הזו תאזין להודעות שיישלחו ל-Service Worker מ-workbox-window עם ערך type של SKIP_WAITING, ובמקרה כזה, תבוצע שיחה אל self.skipWaiting(). שליחת ההודעה הזו היא באמצעות ה-method messageSkipWaiting() ב-workbox-window, שמופיעה בדוגמת הקוד הקודמת.

צריך להציג הודעה?

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

  • יש לך שימוש נרחב לשמירה במטמון. כשמדובר בנכסים סטטיים, יכול להיות שתיתקלו בבעיות בהמשך אם תשתמשו באסטרטגיה שמתמקדת ברשת או רק ברשת לבקשות ניווט, אבל בטעינה מדורגת של נכסים סטטיים. המצב הזה יכול לגרום למצבים שבהם נכסים בעלי גרסאות עשויים להשתנות, והם לא נשמרו מראש במטמון. הצעה של לחצן טעינה מחדש כאן עשויה למנוע התנהגויות לא צפויות.
  • אם אתם מציגים HTML שנשמר מראש במטמון. במקרה כזה כדאי מאוד להציע לחצן טעינה מחדש בעדכוני Service Worker, מאחר שעדכונים של אותו HTML לא יזוהו עד שה-Service Worker המעודכן ישלוט בשליטה.
  • אם אתם לא מסתמכים בעיקר על שמירה במטמון בזמן הריצה. כשמשאבים נשמרים במטמון בזמן הריצה, לא צריך ליידע את המשתמש שצריך לטעון מחדש. בהתאם לשינויים בנכסים עם גרסאות, הם יתווספו למטמון של סביבת זמן הריצה בהתאם לגרסה, בהנחה שבקשות ניווט מבוססות על רשת או על רשת בלבד.
  • כשמשתמשים באסטרטגיה של לא פעיל בזמן אימות מחדש, כדאי להשתמש במודול workbox-broadcast-update כדי להודיע למשתמשים על עדכונים של Service Worker.

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