Максим Сальников
@webmaxru
Погружение в глубокий офлайн –
веб на это способен!
Что значит готовность веб-приложения к офлайн
И как еe достичь прямо сегодня
Максим Сальников
-
Организатор Mobile / Web / PWA митапов в Осло и Лондоне
-
Организатор конференций Mobile Era и ngVikings в Скандинавии
-
Ответственный за поток о веб-разработке 404fest в Самаре
Ответственный за успех Azure-разработчиков в Microsoft
-
The World Wide Web is the New Software Platform
-
The Web Browser is the New Operating System
-
JavaScript is the de facto Programming Language of the Web
Январь, 2008
Веб – отличная платформа для приложений
Браузеры
-
Почти на каждом устройстве с UI
-
Автообновляемые
-
API для доступа к аппаратным ресурсам
JavaScript
Браузеры
-
Разносторонний язык
-
Мощный инструментарий
-
Поступательно развивается в правильном направлении
Веб – отличная платформа для приложений
JavaScript
Движки JS
Браузеры
-
Курс на производительность
-
Возможности встраивания
Веб – отличная платформа для приложений
JavaScript
Движки JS
Интерфейсы
Браузеры
-
Удобные инструменты для создания адаптивных интерфейсов
-
Повышенное внимание доступности
-
Разнообразие высококачественных библиотек компонентов
Веб – отличная платформа для приложений
Сообщество
JavaScript
Движки JS
Интерфейсы
Браузеры
69.7%
Веб – отличная платформа для приложений
Сообщество
JavaScript
Движки JS
Интерфейсы
Браузеры
Проблемы?
Завязан на состояние подключения к сети
(по историческим причинам)
Веб – отличная платформа для приложений
Решения
Кэширование
Установка
-
HTTP Cache
-
AppCache
-
Save page as... (complete)
-
Chrome Apps
-
Electron
-
NativeScript, React Native
Еще одно определение PWA
PWA используют современные веб-API вкупе со стратегией прогрессивного улучшения для создания кросс-платформенных приложений.
Такие приложения запускаются везде и обладают рядом характеристик, обеспечивающих пользователей преимуществами, аналогичными тем, что доступны в нативных решениях.
работают везде*
* но не все возможности доступны везде**
нативно
** применяем стратегию прогрессивного улучшения
=
+
Application shell
Web App Manifest
Быстрые, адаптивные, mobile-first
Работают по HTTPS
Цель офлайнизации №1
Оболочка приложения
(Application Shell)
Спроектируем App shell
My App
-
Определим минимальный, но достаточный набор ресурсов, помним о версионности
-
При первой загрузке явно поместим эти ресурсы в Cache Storage
-
При последующих запусках будем пробовать выдавать эти ресурсы из кэша (иначе – сеть)
-
В то же время проверяем, не обновилась ли версия. Если да – обновляем кэш.
Сервис-воркеры
Логически
Физически
-файл(ы)
Вебсайт
Сервис-воркер
Браузер/ОС
Event-driven worker
Кэш
fetch
push
sync
Предварительное кэширование
self.addEventListener('install', event => {
// Помещаем ресурсы app shell в Cache Storage
})
self.addEventListener('activate', event => {
// Удаляем из Cache Storage устаревшие версии
})
handmade-service-worker.js
В реальности...
-
Необходимо следить за актуальностью списка ресурсов
-
Для некоторых видов ответов (opaque, redirected) потребуется специальная обработка
-
Пересоздание полной версии приложения в кэше при изменении любого из ресурсов неоптимально
-
Размеры кэша ограничены — нужен контроль
-
Придется подумать о механизмах инвалидации кэша
-
...
Перехватываем запросы
self.addEventListener('fetch', event => {
if (event.request.url.indexOf('/api') != -1) {
event.respondWith(
// Реализуем стратегию Network-First (для API?)
)
} else {
event.respondWith(
// Реализуем стратегию Cache-First (для app shell?)
)
}
})
handmade-service-worker.js
В реальности...
-
Реализация стратегий — не самая простая задача
-
Могут потребоваться и более сложные варианты, например, Stale-While-Revalidate
-
Даже в рамках одной стратегии для разных групп ресурсов могут потребоваться разные настройки
-
В итоге придется сделать собственную реализацию роутинга
-
Необходимо предусмотреть все варианты фолбеков
-
...
-
Оболочка приложения
-
Кеширование данных (runtime)
-
Повторение совершенных офлайн запросов
-
Двусторонняя коммуникация с приложением
-
Офлайн-аналитика (GA)
Все это в собственном сервис-воркере
Режимы работы
-
Workbox CLI
-
Плагин Webpack
-
Модуль для NodeJS
# Устанавливаем модуль Workbox для NodeJS
$ npm install workbox-build --save-dev
Билд-скрипт
// Используем режим injectManifest
const {injectManifest} = require('workbox-build')
// Конфигурируем (детали на следующем слайде)
var workboxConfig = {...}
// Вызываем метод и выводим результаты
injectManifest(workboxConfig).then(({count, size}) => {
console.log(`Создан ${workboxConfig.swDest}, который
закеширует ${count} файлов, ${size} байт.`)
})
workbox-build-inject.js
Конфигурация
var workboxConfig = {
globDirectory: 'dist/',
globPatterns: [
'**/*.{txt,png,ico,html,js,json,css}'
],
swSrc: 'src/workbox-service-worker.js',
swDest: 'dist/sw.js'
}
workbox-build-inject.js
Собственно, сам сервис-воркер
// Импортируем библиотеку Workbox с Google CDN
importScripts('https://googleapis.com/.../workbox-sw.js');
// Все остальное делается за нас
workbox.precaching.precacheAndRoute([])
src/workbox-service-worker.js
-
Предварительное кеширование и управление версиями
-
Перехват нужных запросов и выдача ресурсов из Cache Storage или сети
Что мы "инжектим"
[
{
"url": "index.html",
"revision": "34c45cdf166d266929f6b532a8e3869e"
},
{
"url": "favicon.ico",
"revision": "b9aa7c338693424aae99599bec875b5f"
},
...
]
Совместим с билдом приложения
{
"scripts": {
"build-pwa": "npm run build-app &&
node workbox-build-inject.js"
}
}
package.json
Цель офлайнизации №2
Данные приложения
Перехват запросов
self.addEventListener('fetch', event => {
if (event.request.url.indexOf('/api/breakingnews') != -1) {
event.respondWith(
// Реализуем стратегию Network-First
)
} else if (event.request.url.indexOf('/api/archive') != -1 {
event.respondWith(
// Реализуем стратегию Cache-First
)
}
})
handmade-service-worker.js
Роутинг и стратегии
workbox.routing.registerRoute(
new RegExp('/api/breakingnews'),
new workbox.strategies.NetworkFirst()
);
src/workbox-service-worker.js
workbox.routing.registerRoute(
new RegExp('/api/archive'),
new workbox.strategies.CacheFirst({
plugins: [...]
})
);
Стратегии
-
CacheFirst
-
CacheOnly
-
NetworkFirst
-
NetworkOnly
-
StaleWhileRevalidate
Плагины
-
Expiration
-
CacheableResponse
-
BroadcastUpdate
-
BackgroundSync
-
...ваши собственные?
Цель офлайнизации №3
Сохраняем и повторяем действия, совершенные офлайн
Фоновая синхронизация
-
Откладывает действия до тех пор, пока не будет установлено подключение
-
Синхронизация будет выполнена, даже если приложение уже закрыто
navigator.serviceWorker.ready.then( swRegistration => {
return swRegistration.sync.register('myFirstSync');
});
main.js
self.addEventListener('sync', event => {
if (event.tag == 'myFirstSync') {
event.waitUntil(
// То, что было запланировано
);
}
});
handmade-service-worker.js
const postTweetPlugin =
new workbox.backgroundSync.Plugin('tweetsQueue', {
maxRetentionTime: 24 * 60 // Лимит времени
})
src/workbox-service-worker.js
workbox.routing.registerRoute(
/(http[s]?:\/\/)?([^\/\s]+\/)post-tweet/,
new workbox.strategies.NetworkOnly({
plugins: [postTweetPlugin]
}),
'POST'
)
Цель офлайнизации №4
Управляем скачиванием и закачиванием и их результатами
Фоновая загрузка
-
Приостановка / возобновление загрузки при нестабильном подключении
-
Доступ к загруженным ресурсам и статусу загрузки из приложения
-
Продолжение загрузки после закрытия приложения
-
Нативные элементы интерфейса браузера / ОС для управления и получения статуса загрузки
const registration = await navigator.serviceWorker.ready;
await registration.backgroundFetch.fetch(
'my-series',
['s01e01.mpg', 's01e02.mpg'],
{
title: 'Загрузка My Series',
downloadTotal: 1000000000
}
);
main.js
const bgFetches =
await registration.backgroundFetch.getIds();
console.log(bgFetches);
addEventListener('backgroundfetchsuccess', event => {
event.waitUntil(
(async function() {
try {
// Сохраняем результаты в Cache Storage
...
await event.updateUI({ title: `Загрузка завершена` });
} catch (err) {
await event.updateUI({ title: `Ошибка загрузки: ${err}` });
}
})()
);
});
src/service-worker.js
Подробнее и с примерами
Итого
-
Веб сегодня — полноценная платформа для приложений
-
Основы механизмов для работы офлайн доступны в стабильных версиях всех современных браузеров
-
Есть замечательные инструменты для автоматизации типовых задач подготовки к офлайн
-
Главный приоритет — обеспечение отличного пользовательского опыта
"Progressive Web Apps State of the Union" от Dominick Ng на BlinkOn 10
-
2000+ разработчиков
-
Представители основных браузеров, библиотек, фреймворков
-
Все о PWA на русском языке
Спасибо!
@webmaxru
Максим Сальников
Есть вопрос?
@webmaxru
Максим Сальников
#WSH?
Погружение в глубокий офлайн - веб способен на это!
By Maxim Salnikov
Погружение в глубокий офлайн - веб способен на это!
Прогрессивные веб-приложения уже получили действительно широкую известность и признание всеми вовлеченными сторонами: разработчиками браузеров (наконец, всеми!), разработчиками, пользователями. Идея приложений, не зависящих от подключения к сети, доказала свою жизнеспособность, и мы видим все больше и больше проектов, идущих по этому пути, что делает возможность работы в офлайне не только лучшей практикой, но просто и хорошей манерой в вебе. В моем докладе, основанном на глубоком исследовании возможностей Service Worker API (с использованием Cache Storage, Background Fetch, Background Sync) и собранных UX-находках, мы рассмотрим историю офлайн веба, важность рассмотрения подключения как привилегии, текущие проблемы (и их решения) и правильные инструменты. В течение докдада мы спроектируем приложение, готовое к работе офлайн, применяя лучшие технологии и UX-практики и добавляя возможности одна за одной: оболочка приложения, кеширование ресурсов и данных, синхронизация при подключении к сети. Все ради наших пользователей, которые требуют нового уровня отказоустойчивости и скорости работы наших приложений.
- 4,000