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

כברירת מחדל, לפי מחזור החיים של קובץ השירות (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(), כלומר, הוא יתחיל לפעול. לאחר ההפעלה, קובץ השירות החדש ישתלט על כל הלקוחות הקיימים, ויפעיל את האירוע 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(). השיטה messageSkipWaiting() ב-workbox-window, המוצגת בדוגמת הקוד הקודמת, אחראית לשליחת ההודעה הזו.

האם צריך להציג הנחיה?

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

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

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