Создайте PWA с помощью Webpack и Workbox
Опубликовано: 2022-03-10Прогрессивное веб-приложение (PWA) – это сайт, использующий современные технологии для предоставления приложений в Интернете. Это общий термин для новых технологий, таких как «манифест веб-приложения», «рабочий сервис» и т. д. Объединенные вместе, эти технологии позволяют вам быстро и увлекательно работать с вашим веб-сайтом.
Эта статья представляет собой пошаговое руководство по добавлению сервисного работника на существующий одностраничный веб-сайт. Service Worker позволит вам заставить ваш сайт работать в автономном режиме, а также уведомлять ваших пользователей об обновлениях вашего сайта. Обратите внимание, что это основано на небольшом проекте, который связан с Webpack, поэтому мы будем использовать подключаемый модуль Workbox Webpack (Workbox v4).
Рекомендуется использовать инструмент для создания сервис-воркера, поскольку он позволяет эффективно управлять кэшем. Мы будем использовать Workbox — набор библиотек, упрощающих генерацию кода вашего сервис-воркера — для создания нашего сервис-воркера в этом руководстве.
В зависимости от вашего проекта вы можете использовать Workbox тремя различными способами:
- Доступен интерфейс командной строки , который позволяет интегрировать workbox в любое приложение, которое у вас есть;
- Доступен модуль Node.js , который позволяет интегрировать workbox в любой инструмент сборки Node, такой как gulp или grunt;
- Доступен плагин webpack , который позволяет легко интегрироваться с проектом, созданным с помощью Webpack.
Webpack — это сборщик модулей. Для упрощения вы можете думать об этом как об инструменте, который управляет вашими зависимостями JavaScript. Он позволяет импортировать код JavaScript из библиотек и объединяет ваш JavaScript в один или несколько файлов.
Для начала клонируйте следующий репозиторий на свой компьютер:
git clone [email protected]:jadjoubran/workbox-tutorial-v4.git cd workbox-tutorial-v4 npm install npm run dev
Затем перейдите по https://localhost:8080/
. Вы должны увидеть приложение валюты, которое мы будем использовать в этом руководстве:
Начните с оболочки приложения
Оболочка приложения (или «оболочка приложения») — это шаблон, вдохновленный Native Apps. Это поможет придать вашему приложению более естественный вид. Он просто предоставляет приложению макет и структуру без каких-либо данных — переходный экран, предназначенный для улучшения процесса загрузки вашего веб-приложения.
Вот несколько примеров оболочек приложений из нативных приложений:
А вот примеры оболочек приложений из PWA:
Пользователям нравится загрузка оболочек приложений, потому что они презирают пустые экраны. Пустой экран заставляет пользователя чувствовать, что сайт не загружается. Это заставляет их чувствовать, что сайт застрял.
Оболочки приложений пытаются как можно быстрее отрисовать навигационную структуру приложения, такую как панель навигации, панель вкладок, а также загрузчик, указывающий, что запрошенный контент загружается.
Итак, как создать оболочку приложения?
Шаблон оболочки приложения отдает приоритет загрузке HTML, CSS и JavaScript, которые будут отображаться первыми. Это означает, что нам нужно дать этим ресурсам полный приоритет, поэтому вы должны встроить эти активы. Таким образом, чтобы создать оболочку приложения, вам просто нужно встроить HTML, CSS и JavaScript, которые отвечают за оболочку приложения. Конечно, вы не должны встраивать все, а лучше оставаться в пределах от 30 до 40 КБ всего.
Вы можете увидеть встроенную оболочку приложения в index.html . Вы можете проверить исходный код, просмотрев файл index.html , и вы можете просмотреть его в браузере, удалив элемент <main>
в инструментах разработки:
Работает ли он в автономном режиме?
Давайте смоделируем переход в автономный режим! Откройте DevTools, перейдите на вкладку «Сеть» и установите флажок «Не в сети». Когда вы перезагрузите страницу, вы увидите, что мы получим автономную страницу браузера.
Это связано с тем, что первоначальный запрос к /
(который загрузит файл index.html ) завершится ошибкой, поскольку Интернет отключен. Единственный способ для нас восстановиться после этого сбоя запроса — использовать сервисного работника.
Давайте визуализируем запрос без сервис-воркера:
Сервисный работник — это программируемый сетевой прокси, который находится между вашей веб-страницей и Интернетом. Это позволяет контролировать входящие и исходящие сетевые запросы.
Это полезно, потому что теперь мы можем перенаправить этот неудачный запрос в кеш (при условии, что у нас есть содержимое в кеше).
Service Worker также является типом Web Worker, что означает, что он работает отдельно от вашей главной страницы и не имеет доступа ни к window
, ни к объекту document
.
Предварительно кэшировать оболочку приложения
Чтобы наше приложение работало в автономном режиме, мы начнем с предварительного кэширования оболочки приложения.
Итак, начнем с установки плагина Webpack Workbox:
npm install --save-dev workbox-webpack-plugin
Затем мы собираемся открыть наш файл index.js и зарегистрировать сервис-воркера:
if ("serviceWorker" in navigator){ window.addEventListener("load", () => { navigator.serviceWorker.register("/sw.js"); }) }
Затем откройте файл webpack.config.js и давайте настроим плагин Workbox webpack:
//add at the top const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); //add inside the plugins array: plugins: [ … , new WorkboxWebpackPlugin.InjectManifest({ swSrc: "./src/src-sw.js", swDest: "sw.js" }) ]
Это укажет Workbox использовать наш файл ./src/src-sw.js в качестве основы. Сгенерированный файл будет называться sw.js и находиться в папке dist
.
Затем создайте файл ./src/src-sw.js на корневом уровне и напишите в нем следующее:
workbox.precaching.precacheAndRoute(self.__precacheManifest);
Примечание . Переменная self.__precacheManifest
будет импортирована из файла, который будет динамически создан рабочим блоком.
Теперь вы готовы создать свой код с помощью npm run build
, и Workbox сгенерирует два файла в папке dist
:
- предварительный кэш-манифест.66cf63077c7e4a70ba741ee9e6a8da29.js
- sw.js
sw.js импортирует workbox из CDN, а также precache-manifest.[chunkhash].js .
//precache-manifest.[chunkhash].js file self.__precacheManifest = (self.__precacheManifest || []).concat([ "revision": "ba8f7488757693a5a5b1e712ac29cc28", "url": "index.html" }, "url": "main.49467c51ac5e0cb2b58e.js" ]);
В манифесте предварительного кэша перечислены имена файлов, обработанных веб-пакетом и попавших в папку dist
. Мы будем использовать эти файлы для их предварительного кэширования в браузере. Это означает, что когда ваш веб-сайт загружается в первый раз и регистрирует работника службы, он кэширует эти активы, чтобы их можно было использовать в следующий раз.
Вы также можете заметить, что некоторые записи имеют «ревизию», а другие — нет. Это связано с тем, что редакцию иногда можно вывести из хеш-функции из имени файла. Например, давайте подробнее рассмотрим имя файла main.49467c51ac5e0cb2b58e.js . У него есть ревизия в имени файла, который является хэшем 49467c51ac5e0cb2b58e .
Это позволяет Workbox понимать, когда ваши файлы изменяются, поэтому он очищает или обновляет только те файлы, которые были изменены, а не сбрасывает весь кеш каждый раз, когда вы публикуете новую версию своего сервис-воркера.
При первой загрузке страницы сервис-воркер установится. Вы можете увидеть это в DevTools. Сначала запрашивается файл sw.js , который затем запрашивает все остальные файлы. Они четко обозначены значком шестеренки.
Таким образом, Workbox инициализируется и предварительно кэширует все файлы, которые находятся в предварительном кэше-манифесте. Важно еще раз проверить, нет ли в файле precache-manifest ненужных файлов, таких как файлы .map или файлы, не являющиеся частью оболочки приложения.
Во вкладке сети мы видим запросы, поступающие от сервис-воркера. И теперь, если вы попытаетесь выйти в автономный режим, оболочка приложения уже предварительно кэшируется, поэтому она работает, даже если мы не в сети!
Кэширование динамических маршрутов
Вы заметили, что когда мы отключились, оболочка приложения работает, но не наши данные? Это связано с тем, что эти вызовы API не являются частью предварительно кэшированной оболочки приложения . При отсутствии подключения к Интернету эти запросы не будут выполнены, и пользователь не сможет увидеть информацию о валюте.
Однако эти запросы нельзя предварительно кэшировать, поскольку их значение исходит от API. Более того, когда вы начинаете иметь несколько страниц, вы не хотите кэшировать все запросы API за один раз. Вместо этого вы хотите кэшировать их, когда пользователь посещает эту страницу.
Мы называем это «динамическими данными». Они часто включают вызовы API, а также изображения и другие ресурсы, которые запрашиваются, когда пользователь выполняет определенное действие на вашем веб-сайте (например, когда он переходит на новую страницу).
Вы можете кэшировать их с помощью модуля маршрутизации Workbox. Вот как:
//add in src/src-sw.js workbox.routing.registerRoute( /https:\/\/api\.exchangeratesapi\.io\/latest/, new workbox.strategies.NetworkFirst({ cacheName: "currencies", plugins: [ new workbox.expiration.Plugin({ maxAgeSeconds: 10 * 60 // 10 minutes }) ] }) );
Это настроит динамическое кэширование для любого URL-адреса запроса, который соответствует URL-адресу https://api.exchangeratesapi.io/latest
.
Стратегия кэширования, которую мы здесь использовали, называется NetworkFirst
; есть два других, которые часто используются:
-
CacheFirst
-
StaleWhileRevalidate
CacheFirst
будет искать его в кеше. Если он не найден, то он получит его из сети. StaleWhileRevalidate
отправится в сеть и в кеш одновременно. Верните ответ кеша на страницу (в фоновом режиме), он будет использовать новый сетевой ответ для обновления кеша в следующий раз, когда он будет использоваться.
В нашем случае нам пришлось использовать NetworkFirst
, потому что мы имеем дело с курсами валют, которые меняются очень часто. Однако, когда пользователь выходит из сети, мы можем, по крайней мере, показать ему ставки, какие они были 10 минут назад — поэтому мы использовали плагин истечения с maxAgeSeconds
, установленным на 10 * 60
секунд.
Управление обновлениями приложений
Каждый раз, когда пользователь загружает вашу страницу, браузер запускает код navigator.serviceWorker.register
, даже если сервис-воркер уже установлен и запущен. Это позволяет браузеру определить, есть ли новая версия работника службы. Когда браузер замечает, что файл не изменился, он просто пропускает вызов регистрации. Как только этот файл изменяется, браузер понимает, что существует новая версия сервис-воркера, поэтому он устанавливает новый сервис-воркер параллельно с текущим сервис-воркером .
Однако он приостанавливается на этапе installed/waiting
, поскольку одновременно может быть активирован только один сервис-воркер.
Только когда все окна браузера, контролируемые предыдущим сервис-воркером, закрыты, активация нового сервис-воркера становится безопасной.
Вы также можете управлять этим вручную, вызвав skipWaiting()
(или self.skipWaiting()
, поскольку self
является глобальным контекстом выполнения в сервис-воркере). Однако в большинстве случаев вы должны делать это только после того, как спросите пользователя, хотят ли они получить последнее обновление.
К счастью, workbox-window
помогает нам в этом. Это новая оконная библиотека, представленная в Workbox v4, которая направлена на упрощение общих задач на стороне окна.
Начнем с установки со следующего:
npm install workbox-window
Затем импортируйте Workbox
вверху файла index.js :
import { Workbox } from "workbox-window";
Затем мы заменим наш регистрационный код следующим:
if ("serviceWorker" in navigator) { window.addEventListener("load", () => { const wb = new Workbox("/sw.js"); wb.register(); }); }
Затем мы найдем кнопку обновления с идентификаторомworkbox-waiting
:
//add before the wb.register() const updateButton = document.querySelector("#app-update"); // Fires when the registered service worker has installed but is waiting to activate. wb.addEventListener("waiting", event => { updateButton.classList.add("show"); updateButton.addEventListener("click", () => { // Set up a listener that will reload the page as soon as the previously waiting service worker has taken control. wb.addEventListener("controlling", event => { window.location.reload(); }); // Send a message telling the service worker to skip waiting. // This will trigger the `controlling` event handler above. wb.messageSW({ type: "SKIP_WAITING" }); }); });
Этот код будет отображать кнопку обновления при наличии нового обновления (то есть, когда сервис-воркер находится в состоянии ожидания) и отправляет сервис- SKIP_WAITING
сообщение SKIP_WAITING.
Нам потребуется обновить файл сервис-воркера и обработать событие SKIP_WAITING
таким образом, чтобы оно skipWaiting
:
//add in src-sw.js addEventListener("message", event => { if (event.data && event.data.type === "SKIP_WAITING") { skipWaiting(); });
Теперь запустите npm run dev
и перезагрузите страницу. Войдите в свой код и обновите заголовок панели навигации до «Navbar v2». Перезагрузите страницу еще раз, и вы сможете увидеть значок обновления.
Подведение итогов
Наш сайт теперь работает в автономном режиме и может сообщать пользователю о новых обновлениях. Однако имейте в виду, что наиболее важным фактором при создании PWA является пользовательский опыт. Всегда сосредотачивайтесь на создании удобного для пользователей опыта. Мы, как разработчики, слишком увлекаемся технологиями и часто забываем о наших пользователях.
Если вы хотите сделать еще один шаг, вы можете добавить манифест веб-приложения, который позволит вашим пользователям добавлять сайт на главный экран. А если вы хотите узнать больше о Workbox, вы можете найти официальную документацию на веб-сайте Workbox.
Дальнейшее чтение на SmashingMag:
- Можете ли вы заработать больше денег с помощью мобильного приложения или PWA?
- Подробное руководство по прогрессивным веб-приложениям
- Native и PWA: выбор, а не претенденты!
- Создание PWA с использованием Angular 6