Добро пожаловать на 25-й день #30DaysOfPWA! Новичок в этой серии? Три вещи, которые вы можете сделать, чтобы наверстать упущенное:
- Прочитайте начальный пост ниже для понимания контекста.
- Следите за обновлениями в репозитории GitHub.
- Добавьте блог #30DaysOfPWA в закладки для ресурсов.


Добро пожаловать на #30DaysOfPWA
Nitya Narasimhan, Ph.D. для Microsoft Azure ・ Feb 14 ・ 2 min read
Это сокращенная версия канонического поста для #30DaysOfPWA.
Об авторе
Автор сегодняшнего поста — Зак Тутч (Zach Teutsch) — инженер-программист в Microsoft, специализирующийся на разработке Windows Native и PWA. Следите за Заком по адресу @devteutsch.
Добро пожаловать на 4-ю неделю 4-го дня серии «30 дней PWA»! Сегодняшняя статья посвящена лучшим практикам надежности PWA. Если вы ищете основы автономного поведения и кэширования в PWA, ознакомьтесь со статьей Заставить PWA работать автономно с первой недели.
Как выглядит надежность для PWA?
В общем, надежность означает, может ли ваше приложение поддерживать постоянный пользовательский опыт при различных обстоятельствах. Некоторые из вопросов, которые можно отнести к надежности:
- Как быстро загружается ваше приложение при различной прочности соединения
- Как ваше приложение работает в автономных условиях
- Как работает ваше приложение и сервисы при масштабировании и увеличении объема использования
- Как ваше приложение работает на старых и менее мощных устройствах.
Для PWA вопросы надежности, связанные с интернет-соединением пользователя, представляют особый интерес, поскольку более традиционный веб-опыт сильно зависел от наличия соединения.
Сегодня мы рассмотрим то, что мы узнали о кэшировании в Неделе 1, чтобы увидеть, как мы можем воспользоваться возможностями PWA, чтобы наши приложения работали хорошо независимо от силы или доступности интернет-соединения пользователя.
Ускорение работы PWA с помощью стратегий кэширования
Одним из способов ускорения работы PWA и повышения надежности является минимизация запросов к сети и использование кэшированных ответов, когда это возможно. Существует множество стратегий кэширования, которыми вы можете воспользоваться, но сегодня мы рассмотрим только два популярных варианта: cache-first
и stale-while-revalidate
.
Cache-First
В статье Заставить PWA работать в автономном режиме из Недели 1 мы немного рассказали о том, как работает cache-first. Стратегия cache-first — это именно то, что звучит: при событии fetch
наш сервисный работник сначала проверит кэш на наличие ответа и обратится к сети, только если кэш не работает.
Мы видели этот фрагмент сервисного работника:
self.addEventListener('fetch', event => {
event.respondWith((async () => {
const cache = await caches.open(CACHE_NAME);
// Try the cache first.
const cachedResponse = await cache.match(event.request);
if (cachedResponse !== undefined) {
return cachedResponse;
} else {
// Nothing in cache, let's go to the network.
}
}
}
Cache-first — это отличная стратегия для быстрой минимизации сетевых запросов, но она может быть немного наивной и ограничивающей, если применяется слишком широко к вашему приложению.
Для статичных, долговременных ресурсов кэш-первый работает отлично. Однако для быстро меняющихся ресурсов стратегия «кэш-первый» может помешать данным получить столь необходимые обновления, в результате чего пострадает пользовательский опыт.
Cache-first — это отличная отправная точка для повышения надежности производительности, но давайте рассмотрим кое-что более гибкое.
Stale-While-Revalidate
stale-while-revalidate
основывается на cache-first, чтобы дать нам лучшее из обоих миров: скорость загрузки и свежесть.
Как и cache-first, эта стратегия проверяет кэш на наличие нужного ответа и возвращает совпадение. Однако вместо того, чтобы остановиться на этом, мы все равно передадим запрос в сеть и обновим кэш в фоновом режиме с полученным ответом. Если кэш пропущен, мы сразу отправимся в сеть.
Давайте обновим наш фрагмент рабочего сервиса, чтобы отразить это:
self.addEventListener('fetch', event => {
event.respondWith((async () => {
const cache = await caches.open(CACHE_NAME);
// Try the cache first like last time.
const cachedResponse = await cache.match(event.request);
if (cachedResponse !== undefined) {
// Now, we fetch a new response and cache it in the background
fetch(event.request).then( response => {
cache.put(event.request, response.clone());
});
// We don't await the above line, so we return our cachedResponse right away
return cachedResponse;
} else {
// Go to the network otherwise
}
}
}
При таком подходе мы получаем преимущество в скорости загрузки кэшированного ответа сразу же, но балансируем его, поддерживая наш кэш в относительно актуальном состоянии. В следующий раз, когда мы сделаем этот запрос, мы получим обновленный ответ, а не один и тот же кэшированный ответ снова и снова.
Сохранение данных в автономном режиме с помощью IndexedDB
Кэширование отлично работает для активов, но как насчет хранения структурированных локальных данных? Мы можем использовать IndexedDB для хранения локальных данных, чтобы максимально сохранить пользовательский опыт в автономном режиме. IndexedDB позволяет асинхронно хранить большие объемы структурированных данных, которые превышают возможности нашего кэша или LocalStorage.
API для IndexedDB может быть немного низкоуровневым для обычного использования, поэтому мы рассмотрим простую обертку под названием LocalForage.
Использование LocalForage для управления данными
Мы можем использовать гипотетический пример, чтобы увидеть, как мы можем использовать LocalForage в приложении.
Представим, что мы работаем над простым почтовым клиентом, и хотим сохранить X последних писем, чтобы в случае потери соединения с PWA пользователь мог прочитать свои последние письма.
Примечание: IndexedDB можно использовать как внутри приложения, так и в рабочем сервисе, выбирайте то, что лучше всего подходит для вашего случая.
Сначала мы можем создать экземпляр (такой же, как база данных) localforage и дать ему имя:
// our DB name
const databaseName = "EmailDB"
// create an instance with our name
var emailDatabase = localforage.createInstance({name: databaseName});
Теперь давайте добавим в него наши данные с помощью пары ключ-значение:
// Let's get our recent emails as an array
var emails = GetRecentEmails();
// and add that to our database
emailDatabase.setItem("recent-emails", emails);
Позже мы сможем получить наши электронные письма с помощью ключа и сделать что-нибудь со значением:
// Asynchronously fetch our emails and then populate our inbox
emailDatabase.getItem("recent-emails").then( emails => {
PopulateInbox(emails);
});
И, наконец, мы можем так же легко удалить данные:
// remove based on key
emailDatabase.removeItem("recent-emails");
Очень просто, правда? Для простоты использования LocalForage ограничивает свой API всего несколькими функциями, и вы можете узнать больше о его возможностях здесь. Это отличный вариант для начала работы с IndexedDB и хранения данных для простых случаев использования.
Если вам нужно что-то более гибкое для вашего PWA, есть также idb, который является еще одной отличной оберткой для IndexedDB, обладающей большей функциональностью. Однако, она более сложна в управлении базой данных и транзакциями, так что будьте готовы к большим сложностям. Если вам нравится стиль idb, но вы хотите что-то более простое, чем LocalForage, есть также idb-keyval, который поддерживается создателем idb и имеет меньший размер пакета, чем LocalForage.
Подведение итогов
Вот и все о лучших практиках надежности PWA! Еще многое можно сделать для повышения надежности вашего PWA, но базовые стратегии кэширования и IndexedDB являются отличными отправными точками для создания надежного автономного веб-опыта. Единственное, что остается сделать сейчас — это начать кэширование!
Ресурсы
- Making PWA Work Offline
- Поваренная книга по автономной работе
- Документация MDN по IndexedDB
- LocalForage
- idb
Хотите читать больше материалов от технологов Microsoft? Не забудьте следить за Azure прямо здесь, на dev.to:
