Показывать компонент оповещения, когда вы подтолкнули нового сервис-воркера, позволяя пользователю сразу же обновить свою страницу

Create React App (CRA) отлично подходит для разработки прогрессивных веб-приложений (PWA). Он имеет встроенное поведение в автономном режиме / кеширование. По умолчанию он не включен, но вы можете включить его. Он использует сервис-воркеров и имеет множество подводных камней, о которых вы можете прочитать в официальных документах.

Эта статья покажет вам, как вызвать предупреждение (или тост, или любой другой компонент, который вы хотите), когда вы обновили своего сервис-воркера. Обычно это происходит, когда в вашем приложении появляются новые обновления, и вы хотите, чтобы пользователь сразу их увидел.

Предполагается, что у вас есть новый проект, созданный с помощью CRA. Если вы этого не сделаете, вы можете легко это сделать с помощью:

npx create-react-app my-app

Регистрация сервис-воркера

Если вы перейдете к src/index.js, вы найдете в последней строке:

serviceWorker.unregister();

Переключите его на:

serviceWorker.register();

И регистрация сервис-воркера практически завершена. Если вы развернете свое приложение на сайте с поддержкой HTTPS, оно будет кэшировано.

Помните, что реализация сервис-воркера в CRA работает только в продакшене. Вы можете убедиться, что он работает, установив флажок офлайн на вкладке Chrome DevTools Network и перезагрузив страницу.

Он по-прежнему показывает ваше приложение!

Ваш обновленный сервисный работник не виден?

Теперь самое сложное. Вы добавляете или изменяете код в своем приложении и развертываете его, но пользователи не видят ваши обновления. Как говорится в документации:

Поведение по умолчанию - консервативно держать обновленного сервис-воркера в состоянии« ожидания . Это означает, что пользователи будут видеть старый контент, пока они не закроют (перезагрузки недостаточно) свои существующие открытые вкладки ».

Что делать, если вы хотите, чтобы пользователи видели ваши новые обновления, не закрывая все вкладки? CRA также предоставляет такую ​​возможность.

В src/serviceWorker.js находится функция с именем registerValidSW, которая обеспечивает доступ к обновлениям сервис-воркеров и успешным событиям через обратные вызовы, а также выводит информацию об этих событиях на консоль. Так вы узнаете, когда показать, что приложение кэшировано для использования в автономном режиме или доступна более новая версия.

Функция registerValidSW принимает два аргумента: второй - тот, который нас интересует. config может быть объектом, в котором есть обратные вызовы onSuccess и onUpdate. Теперь вы должны задаться вопросом, как и где мы могли бы сделать такой объект?

Если вы посмотрите, где называется registerValidSW, вы увидите, что оно происходит от export function register(config). Это та самая функция, которую мы видели в последней строке src/index.js. Теперь мы вернулись к нашему собственному коду и можем сделать что-то вроде:

serviceWorker.register({
  onSuccess: () => store.dispatch({ type: SW_INIT }),
  onUpdate: reg => store.dispatch({ type: SW_UPDATE, payload: reg }),
});

Когда эти функции вызываются, они отправляют функцию, и вы можете делать с ними все, что хотите, например, показывать сообщение.

С onSuccess это проще - вы можете просто показать оповещение где-нибудь на своей странице. Возможно, там написано: «Страница сохранена для использования в автономном режиме». С onUpdate вы хотите, чтобы пользователь знал, что доступна более новая версия, и можете добавить кнопку с надписью «Нажмите, чтобы получить последнюю версию».

Отображение предупреждения пользователя при первом сохранении страницы для использования в автономном режиме

В приведенном выше примере я использовал хранилище Redux для отправки действия, и у меня есть хранилище, настроенное с помощью:

const initalState = {
  serviceWorkerInitialized: false,
  serviceWorkerUpdated: false,
  serviceWorkerRegistration: null,
}

Теперь при отправке действия типа SW_INIT мы меняем состояние serviceWorkerInitialized на true и можем использовать этот селектор внутри любого компонента React.

В моем src/App.js (или любом другом компоненте) мы получаем его из магазина с помощью Redux Hooks:

const isServiceWorkerInitialized = useSelector(
  state => state.serviceWorkerInitialized
);

И мы можем показать оповещение, когда оно true:

{isServiceWorkerInitialized && (
  <Alert text="Page has been saved for offline use" />
)}

Отображение пользователю предупреждения и кнопки, когда доступна новая версия Service Worker

Используя тот же шаблон, мы показываем компоненты предупреждений, когда сервис-воркеры обновлены.

{isServiceWorkerUpdated && (
  <Alert
    text="There is a new version available."
    buttonText="Update"
    onClick={updateServiceWorker}
  />
)}

На этот раз мы добавляем onClick функцию, которая будет запускаться при нажатии кнопки “Update” внутри компонента предупреждения. Потому что мы хотим, чтобы пользователь щелкнул кнопку и получил новую версию приложения.

Вся магия заключается в функции updateServiceWorker, которую мы собираемся создать.

В этом примере используется CRA v3, в котором в файле public/service-worker.js создано небольшое дополнение. (Если вы используете старую версию CRA, я создал решение и для этого - просто напишите мне.)

skipWaiting - это функция, которая заставляет вашего нового сервис-воркера стать активным, и в следующий раз, когда пользователь откроет браузер и перейдет на вашу страницу, он сможет увидеть новую версию, не выполняя никаких действий.

Подробнее о skipWaiting читайте в MDN. Но это просто заставляет вашего сервис-воркера быть активным, и вы увидите изменения только в следующий раз. Мы хотим убедиться, что у пользователя есть новая версия прямо сейчас. Вот почему мы должны вызвать его, а затем обновить страницу сами - но только после того, как новый сервис-воркер станет активным.

Чтобы вызвать это, нам нужен экземпляр нашего нового сервис-воркера. Если вы прокрутите назад до того места, где мы зарегистрировали сервис-воркера, вы увидите, что функция onUpdate имеет аргумент с именем reg. Это объект регистрации, и это наш экземпляр. Он будет отправлен в свойство serviceWorkerRegistration в магазине Redux, и мы сможем получить наш ожидающий SW от serviceWorkerRegistration.waiting.

Это будет наша функция, которая будет вызываться, когда пользователь нажимает кнопку “Update” внутри предупреждения:

const updateServiceWorker = () => {
  const registrationWaiting = serviceWorkerRegistration.waiting;
  if (registrationWaiting) {
    registrationWaiting.postMessage({ type: 'SKIP_WAITING' });
    registrationWaiting.addEventListener('statechange', e => {
      if (e.target.state === 'activated') {
        window.location.reload();
      }
    });
  }
};

Поскольку сервис-воркер является воркером и, следовательно, находится в другом потоке, для отправки любых сообщений в другой поток мы должны использовать Worker.postMessage (MDN). Тип сообщения 'SKIP_WAITING', как мы видели из сгенерированного файла public/service-worker.js.

И мы создаем eventListener, который ожидает изменения состояния нашего нового сервис-воркера, и когда он активируется, мы сами перезагружаем страницу. Вот и все.

Теперь пользователь может видеть, что доступна более новая версия, и при желании может сразу же обновить ее.

Заключение

Я думаю, это хорошо, если пользователь сам решит, нужна ему новая версия прямо сейчас или нет. У них есть возможность нажать на кнопку “Update” и получить новую версию или просто проигнорировать ее. Затем новая версия приложения будет доступна, когда они закроют свои вкладки и снова откроют ваше приложение.

Спасибо.

Вот ссылка на репозиторий примеров.