Максим Сальников
@webmaxru
Сервис-воркеры:
используем накопленные знания для светлого будущего PWA
Что мы узнали за 3 года использования PWA?
И как жить дальше
Максим Сальников
@webmaxru
-
Google Dev Expert, Microsoft MVP
-
Организатор Mobile / Web / PWA митапов в Осло и Лондоне
-
Организатор конференций Mobile Era и ngVikings
Full-stack разработчик "приложений из будущего" в ForgeRock
#YearOfPWA
Что новенького?
Еще одно определение PWA
PWA используют современные веб-API вкупе со стратегией прогрессивного улучшения для создания кросс-платформенных приложений.
Эти приложения запускаются везде и обладают рядом характеристик, обеспечивающих пользователей преимуществами, аналогичными тем, что доступны в нативных решениях.
Кросс-платформенные?
Браузеры
Настольные
Мобильные
Магазины?
Для разработчиков
Онлайн больше не обязателен
Масса других возможностей
Получение уведомлений
Service Worker API
Web App Manifest
Новые преимущества?
Полноценное приложение
}
Сервис-воркер
Двигатель PWA
Логически
Физически
JS
-файл
Приложение
Сервис-воркер
Инструментарий
-
Service Worker API
-
Cache API
-
IndexedDB
-
Fetch
-
Clients API
-
Broadcast Channel API
-
Push API
-
Notifications API
-
Local Storage
-
Session Storage
-
XMLHttpRequest
-
DOM
Непростой жизненный цикл
'install'
Загрузка
Установка
Активация
Удален
'activate'
Ожидание
Активен
Прогрессивные веб-приложения
стремящийся к прогрессу, проникнутый передовыми идеями
постепенно возрастающий, увеличивающийся
Прогрессивное улучшение
Определяйте наличие функциональности
Совет #1
Регистрация
if ('serviceWorker' in navigator) {
// Регистрируем сервис-воркер
}
Фоновая синхронизация
if ('SyncManager' in window) {
// Реализуем функциональность для оффлайн-режима
}
Подписка на push
if (!('PushManager' in window)) {
// Прячем интерфейс подписки на push-уведомления
}
Действия в уведомлениях
if ('actions' in Notification.prototype) {
// Можем использовать кнопки с разными действиями
}
Правильный момент для регистрации
Чем позже, тем лучше
Совет #2
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw-workbox.js')
.then(...);
}
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw-workbox.js')
.then(...);
)};
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.then(() => {
// Регистрация сервис-воркера
});
main.ts
Если что-то пошло не так
Нужен kill switch
Совет #3
-
Разрегистрировать сервис-воркер?
-
Разместить исправленный сервис-воркер (или no-op)
-
Удостовериться, что сервис-воркер не берется из кеша HTTP
План спасения
No-op
self.addEventListener('install', () => {
self.skipWaiting();
});
self.addEventListener('activate', () => {
self.clients.matchAll({type: 'window'}).then(tabs => {
tabs.forEach(tab => {
tab.navigate(tab.url);
});
});
});
Крайняя мера для UX
Обновление и обход кеша HTTP
Cache-Control: no-cache
Ресурсы, импортированные через importScripts()
Сервис-воркер
Спецификация обновлена
Побайтное сравнение, нужно использовать версионность
navigator.serviceWorker.register(`/sw.js?v=${VERSION}`);
Контент не проверяется, нужно менять название ресурса
updateViaCache
index.html
navigator.serviceWorker.register('/sw.js', {
updateViaCache: 'none'
})
Значения: "imports", "all" или "none"
Предварительное кеширование
Знайте ваши ресурсы и не оставляйте мусор
Совет #4
const appShellFilesToCache = [
...
'./non-existing.html'
]
sw-handmade.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName).then((cache) => {
return cache.addAll(appShellFilesToCache)
})
)
})
-
Ошибки HTTP
-
Время работы сервис-воркера
-
Ошибки хранилищ
if ('storage' in navigator && 'estimate' in navigator.storage) {
navigator.storage.estimate().then(({usage, quota}) => {
console.log(`Using ${usage} out of ${quota} bytes.`);
});
}
Chrome | <6% своб. пространства |
Firefox | <10% своб. пространства |
Safari | <50MB |
IE10 | <250MB |
Edge | Зависит от размера диска |
Объем хранилищ ограничен
Кеширование ресурсов с других адресов
Приготовьтесь
к непрозрачности
Совет #5
2 варианта
-
Добавить заголовки CORS на удаленной стороне
-
Обрабатывать opaque ответы
Ограничения opaque ответов
-
Свойство status всегда равно нулю и не зависит от того, успешен запрос или нет
-
Методы Cache API add()/addAll() сработают аварийно, если статус хотя бы одного из ответов не находится в диапазоне 2XX
const appShellFilesToCache = [
...
'https://workboxjs.org/offline-ga.min.svg'
]
sw-handmade.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName).then((cache) => {
return cache.addAll(appShellFilesToCache)
})
)
})
Решение для no-cors
const noCorsRequest =
new Request('https://workboxjs.org/offline-ga.svg', {
mode: 'no-cors'
});
fetch(noCorsRequest)
.then(response => cache.put(noCorsRequest, response));
Правильные инструменты
Доверяй И проверяй
Совет #6
Инструменты помогают
-
Реализовывать сложные алгоритмы
-
Перенимать лучшие практики
-
Фокусироваться на ВАШЕЙ задаче
-
Следить за обновлениями спецификаций
-
Обрабатывать пограничные случаи
Фреймворки
-
sw-precache / sw-toolbox
-
Workbox
-
offline-plugin для Webpack
-
PWABuilder.com
-
create-react-app
-
preact-cli
-
polymer-cli
-
vue-cli
-
angular-cli
Генераторы
-
Lighthouse
-
Sonarwhal
Аудит
App shell
Динамическое кеширование
Оффлайн GA
Повторение неуспешных запросов
Уведомления об обновлениях
Build-интеграции
Возможность расширять функциональность собственного сервис-воркера
Генерация и валидация Web App Manifest
Генерация сервис-воркера
Генерация графики (иконки)
Генерация нативных проектов для размещения в магазины приложений
npm install -g sonarwhal
sonarwhal --init
sonarwhal https://airhorner.com
npm install -g lighthouse
lighthouse https://airhorner.com
Push-уведомления
Реинкарнации "всплывающих окон" - НЕТ!
Совет #7
-
Только после явного запроса от пользователя
-
Очевидная и простая отписка
Подписка
Уведомления
-
Рассмотреть все альтернативные варианты. Уведомление – крайний случай.
-
Не для массовых, но для индивидуальных сообщений
MyAirline
Открыта регистрация на ваш рейс
MyAirline
myairline.com
Ваш рейс
MY 1228
Отправление
21.09 13:45
SVO -> KUF
Нажмите здесь, чтобы зарегистрироваться
myairline.com
Содержимое
-
Не повторяйтесь. Места и так мало.
-
Отправляйте непосредственно информацию, а не факт ее поступления
-
Продумайте призыв к действию
Не только кеширование
Используйте весь потенциал сервис-воркера
Совет #8
Балансировщик нагрузки
-
Перехватываем запросы и выбираем определенный сервер
-
Например, наименее нагруженный или для A/B тестирования
Загрузки из браузера
service-worker.js
self.addEventListener('fetch', function(event) {
if(event.request.url.indexOf("download-file") !== -1) {
event.respondWith(event.request.formData().then( formdata => {
var response = new Response(formdata.get("filebody"));
response.headers.append('Content-Disposition',
'attachment; filename="' + formdata.get("filename") + '"');
return response;
}));
}
});
Поддержка WebP (с WASM)
service-worker.js
event.respondWith(async function() {
const response = await fetch(event.request);
const buffer = await response.arrayBuffer();
const WebPDecoder = await fetchWebPDecoder();
const decoder = new WebPDecoder(buffer);
const blob = await decoder.decodeToBMP();
return new Response(blob, { headers: { "content-type": "image/bmp",
"status": 200 } });
}());
-
1400+ разработчиков
-
Представители основных браузеров, библиотек, фреймворков
Совет #9
-
Все о PWA на русском языке
Спасибо!
@webmaxru
Максим Сальников
Новые функции
Еще в разработке
Совет #9
Периодическая синхронизация
-
Ограничение временным интервалом, состоянием батареи, режимом подключения к сети
-
Требует явного разрешения со стороны пользователя
-
Не требуется поддержки со стороны сервера
-
Финальное решение по запуску – за браузером
navigator.serviceWorker.ready.then((registration) => {
registration.periodicSync.register({
tag: 'get-latest-news', // default: ''
minPeriod: 12 * 60 * 60 * 1000, // default: 0
powerState: 'avoid-draining', // default: 'auto'
networkState: 'avoid-cellular' // default: 'online'
}).then((periodicSyncReg) => {
// Задача успешно зарегистрирована
})
});
index.html
self.addEventListener('periodicsync', function(event) {
if (event.registration.tag == 'get-latest-news') {
event.waitUntil(fetchAndCacheLatestNews());
}
else {
// Неизвестная задача, лучше отменить регистрацию
event.registration.unregister();
}
});
sw-handmade.js
-
1400+ разработчиков
-
Представители основных браузеров, библиотек, фреймворков
Совет #10
-
Все о PWA на русском языке
Спасибо!
@webmaxru
Максим Сальников
Есть вопрос?
Сервис-воркеры: используем накопленные знания для светлого будущего PWA
By Maxim Salnikov
Сервис-воркеры: используем накопленные знания для светлого будущего PWA
Несомненно, 2018 - это год, когда Progressive Web Apps получат действительно широкое признание всех вовлеченных сторон: разработчиков браузеров, разработчиков веб-приложений, пользователей. Скорость и гладкость этого процесса в значительной степени зависят от того, насколько правильно мы, разработчики, используем возможности новых API. Основа всей идеи PWA - это Service Worker API, который отвечает за всю магию работы оффлайн, оптимизацию сетевых запросов, push-уведомления и массу других интересных и полезных вещей. В моей сессии, основанной на накопленном опыте разработки и поддержки PWA, мы пройдемся по списку: лучшие практики с примерами кода как избежать множества подводных камней последние новости с фронта поддержки PWA браузерами известные ограничения и как с ними жить масса полезной и практической информации о PWA
- 2,202