В этой лабораторной работе показано, как реализовать отказоустойчивый поиск с помощью Workbox. Используемое демо-приложение содержит поле поиска, которое вызывает конечную точку сервера и перенаправляет пользователя на простую HTML-страницу.
Мера
Прежде чем добавлять оптимизации, всегда полезно проанализировать текущее состояние приложения. Проверьте, как сайт ведёт себя при переходе в офлайн-режим:
- Нажмите `Control+Shift+J` (или `Command+Option+J` на Mac), чтобы открыть DevTools.
 - Откройте вкладку Сеть .
 - Откройте Chrome DevTools и выберите панель «Сеть».
 - В раскрывающемся списке Регулирование выберите Автономно .
 - В демо-приложении введите поисковый запрос, затем нажмите кнопку «Поиск» .
 
Отображается стандартная страница ошибки браузера:

Предоставьте запасной вариант ответа
 Service Worker содержит код для добавления автономной страницы в список предварительного кэширования , поэтому ее всегда можно кэшировать при событии install Service Worker.
Обычно вам нужно указать Workbox добавить этот файл в список предварительного кэширования во время сборки, интегрировав библиотеку с выбранным вами инструментом сборки (например, webpack или gulp ).
 Для простоты мы уже сделали это за вас. Следующий код в public/sw.js реализует это:
const FALLBACK_HTML_URL = '/index_offline.html';
…
workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);
Затем добавьте код для использования автономной страницы в качестве резервного ответа:
- Чтобы просмотреть исходный текст, нажмите « Просмотреть исходный текст» .
 -  Добавьте следующий код в конец 
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();
  }
});
Код делает следующее:
- Определяет стратегию «Только сеть» по умолчанию, которая будет применяться ко всем запросам.
 -  Объявляет глобальный обработчик ошибок, вызывая 
workbox.routing.setCatchHandler()для обработки невыполненных запросов. При запросах документов возвращается резервная офлайн-страница HTML. 
Чтобы проверить эту функциональность:
- Вернитесь на другую вкладку, на которой запущено ваше приложение.
 - Установите в раскрывающемся списке Регулирование значение Онлайн .
 - Нажмите кнопку «Назад» в Chrome, чтобы вернуться на страницу поиска.
 - Убедитесь, что флажок Отключить кэш в DevTools отключен.
 - Нажмите и удерживайте кнопку «Перезагрузить» в Chrome и выберите «Очистить кэш и принудительно перезагрузить», чтобы убедиться, что ваш Service Worker обновлен.
 - Снова установите в раскрывающемся списке «Регулирование » значение «Оффлайн» .
 - Введите поисковый запрос и снова нажмите кнопку «Поиск» .
 
Показана резервная HTML-страница:

Запрос разрешения на уведомление
 Для простоты автономная страница views/index_offline.html уже содержит код для запроса разрешений на уведомления в блоке скрипта внизу:
function requestNotificationPermission(event) {
  event.preventDefault();
  Notification.requestPermission().then(function (result) {
    showOfflineText(result);
  });
}
Код делает следующее:
-  Когда пользователь нажимает кнопку «Подписаться на уведомления», вызывается функция 
requestNotificationPermission(), которая, в свою очередь, вызываетNotification.requestPermission()для отображения запроса на разрешение браузера по умолчанию. Промис выполняется с разрешением, выбранным пользователем:granted,denied) илиdefault. -  Передает разрешенное разрешение 
showOfflineText()для показа соответствующего текста пользователю. 
Сохраняйте запросы в автономном режиме и повторяйте попытки, когда снова будете в сети
Затем реализуйте фоновую синхронизацию Workbox для сохранения автономных запросов, чтобы их можно было повторить, когда браузер обнаружит, что соединение восстановлено.
-  Откройте 
public/sw.jsдля редактирования. - Добавьте следующий код в конец файла:
 
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}¬ification=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с добавлением параметра запроса¬ification=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)
  );
});
Протестируйте функцию
- Вернитесь на другую вкладку, на которой запущено ваше приложение.
 - Установите в раскрывающемся списке Регулирование значение Онлайн .
 - Нажмите кнопку «Назад» в Chrome, чтобы вернуться на страницу поиска.
 - Нажмите и удерживайте кнопку «Перезагрузить» в Chrome и выберите «Очистить кэш и принудительно перезагрузить», чтобы убедиться, что ваш Service Worker обновлен.
 - Снова установите в раскрывающемся списке «Регулирование » значение «Оффлайн» .
 - Введите поисковый запрос и снова нажмите кнопку «Поиск» .
 - Нажмите «Подписаться на уведомления» .
 - Когда Chrome спросит вас, хотите ли вы предоставить приложению разрешение на отправку уведомлений, нажмите Разрешить .
 - Введите другой поисковый запрос и снова нажмите кнопку «Поиск» .
 - Снова установите в раскрывающемся списке «Регулирование » значение «Онлайн» .
 
Как только соединение восстановится, появится уведомление:

Заключение
Workbox предоставляет множество встроенных функций, которые сделают ваши PWA более надёжными и интересными. В этой лабораторной работе вы изучили, как реализовать API фоновой синхронизации с помощью абстракции Workbox, чтобы гарантировать, что запросы пользователей, отправленные офлайн, не будут потеряны и их можно будет повторить после восстановления соединения. Демоверсия представляет собой простое поисковое приложение, но вы можете использовать похожую реализацию для более сложных сценариев и вариантов использования, включая чаты, публикацию сообщений в социальных сетях и т. д.