יצירת חוויות חיפוש עמידות באמצעות תיבת עבודה

Demián Renzulli
Demián Renzulli

בשיעור הזה תלמדו איך להטמיע חוויית חיפוש גמישה באמצעות Workbox. אפליקציית ההדגמה שבה הוא משתמש מכילה תיבת חיפוש שקוראת לנקודת קצה של שרת, ומפנה את המשתמש לדף HTML בסיסי.

מדידה

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

  • לוחצים על Remix to Edit כדי להפוך את הפרויקט לעריכה.
  • כדי לראות תצוגה מקדימה של האתר, לוחצים על הצגת האפליקציה ואז על מסך מלא מסך מלא.

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

  1. מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את כלי הפיתוח.
  2. לוחצים על הכרטיסייה רשת.
  3. פותחים את כלי הפיתוח ל-Chrome ובוחרים בחלונית Network (רשת).
  4. ברשימה הנפתחת Throttling, בוחרים באפשרות Offline.
  5. באפליקציית ההדגמה, מזינים שאילתת חיפוש ולוחצים על הלחצן חיפוש.

מוצג דף השגיאה הרגיל של הדפדפן:

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

הגדרת תשובה חלופית

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

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

כדי לפשט את התהליך, כבר עשינו את זה בשבילך. הקוד הבא ב-public/sw.js מבצע את הפעולה הזו:

const FALLBACK_HTML_URL = '/index_offline.html';

workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);

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

  1. כדי לראות את המקור, לוחצים על הצגת המקור.
  2. מוסיפים את הקוד הבא לחלק התחתון של public/sw.js:
workbox.routing.setDefaultHandler(new workbox.strategies.NetworkOnly());

workbox.routing.setCatchHandler(({event}) => {
  switch (event.request.destination) {
    case 'document':
      return caches.match(FALLBACK_HTML_URL);
      break;
    default:
      return Response.error();
  }
});

הקוד מבצע את הפעולות הבאות:

  • ההגדרה הזו מגדירה אסטרטגיה של רשת בלבד כברירת מחדל שתחול על כל הבקשות.
  • הפונקציה מכריזה על handler גלובלי לשגיאות, על ידי קריאה ל-workbox.routing.setCatchHandler() כדי לנהל בקשות שנכשלו. אם הבקשות הן למסמכים, יוחזר דף HTML חלופי במצב אופליין.

כדי לבדוק את הפונקציונליות הזו:

  1. חוזרים לכרטיסייה השנייה שבה האפליקציה פועלת.
  2. ברשימה הנפתחת Throttling (הגבלת קצב העברת נתונים), בוחרים שוב באפשרות Online (באינטרנט).
  3. לוחצים על הלחצן הקודם ב-Chrome כדי לחזור לדף החיפוש.
  4. מוודאים שתיבת הסימון השבתת המטמון בכלי הפיתוח מושבתת.
  5. כדי לוודא ש-service worker מתעדכן, לוחצים לחיצה ארוכה על הלחצן טעינה מחדש של Chrome ובוחרים באפשרות ניקוי מטמון וטעינה מחדש.
  6. ברשימה הנפתחת Throttling (הגבלת קצב העברת נתונים), בוחרים שוב באפשרות Offline (אופליין).
  7. מזינים שאילתת חיפוש ולוחצים שוב על הלחצן חיפוש.

מוצג דף HTML חלופי:

צילום מסך של חוויית המשתמש המותאמת אישית במצב אופליין בדפדפן.

בקשת הרשאה לשליחת התראות

כדי לפשט את התהליך, הדף במצב אופליין בכתובת views/index_offline.html כבר מכיל את הקוד לבקשת הרשאות לשליחת התראות בבלוק סקריפט בחלק התחתון:

function requestNotificationPermission(event) {
  event.preventDefault();

  Notification.requestPermission().then(function (result) {
    showOfflineText(result);
  });
}

הקוד מבצע את הפעולות הבאות:

  • כשהמשתמש לוחץ על subscribe to notifications (הרשמה לקבלת התראות), הפונקציה requestNotificationPermission() מופעלת, והיא מפעילה את הפונקציה Notification.requestPermission() כדי להציג את בקשת ההרשאה שמוגדרת כברירת מחדל בדפדפן. ההבטחה נפתרת עם ההרשאה שהמשתמש בחר, שיכולה להיות granted, denied או default.
  • ההרשאה שנקבעה מועברת אל showOfflineText() כדי להציג למשתמש את הטקסט המתאים.

שמירת שאילתות אופליין וניסיון חוזר כשהחיבור לאינטרנט מתחדש

לאחר מכן, מטמיעים את Workbox Background Sync כדי לשמור שאילתות במצב אופליין, כך שניתן יהיה לנסות אותן שוב כשהדפדפן יזהה שהחיבור חזר.

  1. פותחים את public/sw.js לעריכה.
  2. מוסיפים את הקוד הבא בסוף הקובץ:
const bgSyncPlugin = new workbox.backgroundSync.Plugin('offlineQueryQueue', {
  maxRetentionTime: 60,
  onSync: async ({queue}) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        const response = await fetch(entry.request);
        const cache = await caches.open('offline-search-responses');
        const offlineUrl = `${entry.request.url}&notification=true`;
        cache.put(offlineUrl, response);
        showNotification(offlineUrl);
      } catch (error) {
        await this.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

הקוד מבצע את הפעולות הבאות:

  • workbox.backgroundSync.Plugin מכיל את הלוגיקה להוספת בקשות שנכשלו לתור, כדי שאפשר יהיה לנסות אותן שוב מאוחר יותר. הבקשות האלה יישמרו ב-IndexedDB.
  • הערך maxRetentionTime מציין את משך הזמן שבו אפשר לנסות שוב לשלוח בקשה. במקרה הזה בחרנו 60 דקות (אחרי כן הוא יימחק).
  • onSync הוא החלק החשוב ביותר בקוד הזה. המערכת תקרא לקריאה החוזרת הזו כשהחיבור יחזור, כדי לאחזר את הבקשות שהוכנסו לתור ואז להביא אותן מהרשת.
  • תגובת הרשת מתווספת למטמון offline-search-responses, עם צירוף פרמטר השאילתה &notification=true, כדי שאפשר יהיה לאחזר את רשומת המטמון הזו כשמשתמש לוחץ על ההתראה.

כדי לשלב סנכרון ברקע עם השירות, צריך להגדיר אסטרטגיית NetworkOnly לבקשות לכתובת ה-URL של החיפוש (/search_action) ולהעביר את bgSyncPlugin שהוגדר קודם. מוסיפים את הקוד הבא לחלק התחתון של public/sw.js:

const matchSearchUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return url.pathname === '/search_action' && !(notificationParam === 'true');
};

workbox.routing.registerRoute(
  matchSearchUrl,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
);

ההגדרה הזו אומרת ל-Workbox לגשת תמיד לרשת, ואם הבקשות נכשלות, להשתמש בלוגיקה של סנכרון ברקע.

לאחר מכן, מוסיפים את הקוד הבא לחלק התחתון של public/sw.js כדי להגדיר אסטרטגיית אחסון במטמון לבקשות שמגיעות מהתראות. כדאי להשתמש בשיטת CacheFirst כדי שהם יוצגו מהמטמון.

const matchNotificationUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return (url.pathname === '/search_action' && (notificationParam === 'true'));
};

workbox.routing.registerRoute(matchNotificationUrl,
  new workbox.strategies.CacheFirst({
     cacheName: 'offline-search-responses',
  })
);

לבסוף, מוסיפים את הקוד להצגת ההתראות:

function showNotification(notificationUrl) {
  if (Notification.permission) {
     self.registration.showNotification('Your search is ready!', {
        body: 'Click to see you search result',
        icon: '/img/workbox.jpg',
        data: {
           url: notificationUrl
        }
     });
  }
}

self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  event.waitUntil(
     clients.openWindow(event.notification.data.url)
  );
});

בדיקת התכונה

  1. חוזרים לכרטיסייה השנייה שבה האפליקציה פועלת.
  2. ברשימה הנפתחת Throttling (הגבלת קצב העברת נתונים), בוחרים שוב באפשרות Online (באינטרנט).
  3. לוחצים על הלחצן הקודם ב-Chrome כדי לחזור לדף החיפוש.
  4. כדי לוודא ש-service worker מתעדכן, לוחצים לחיצה ארוכה על הלחצן טעינה מחדש של Chrome ובוחרים באפשרות ניקוי מטמון וטעינה מחדש.
  5. ברשימה הנפתחת Throttling (הגבלת קצב העברת נתונים), בוחרים שוב באפשרות Offline (אופליין).
  6. מזינים שאילתת חיפוש ולוחצים שוב על הלחצן חיפוש.
  7. לוחצים על הרשמה להתראות.
  8. כש-Chrome שואל אם אתם רוצים לתת לאפליקציה הרשאה לשלוח התראות, לוחצים על אישור.
  9. מזינים שאילתת חיפוש אחרת ולוחצים שוב על הלחצן חיפוש.
  10. ברשימה הנפתחת Throttling (הגבלת קצב העברת נתונים), בוחרים שוב באפשרות Online (באינטרנט).

כשהחיבור יחודש, תוצג התראה:

צילום מסך של התהליך המלא במצב אופליין.

סיכום

ב-Workbox יש הרבה תכונות מובנות שיעזרו לכם ליצור אפליקציות PWA עמידות ומושכות יותר. ב-codelab הזה למדתם איך להטמיע את Background Sync API באמצעות Workbox abstraction, כדי להבטיח ששאילתות של משתמשים במצב אופליין לא יאבדו, ושאפשר יהיה לנסות לשלוח אותן שוב כשהחיבור יחזור. ההדגמה היא אפליקציית חיפוש פשוטה, אבל אפשר להשתמש בהטמעה דומה גם בתרחישים ובמקרים מורכבים יותר, כולל אפליקציות צ'אט, פרסום הודעות ברשת חברתית וכו'.