Максим Сальников
@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

Установка приложения "как нативного"

Офлайн-готовность приложения
-
Само приложение
-
Потребляемые данные
-
Действия офлайн
-
Ошибки соединения
-
Обновления
-
Возможности платформы
-
Всегда доступно
-
Осмысленно сохраняются
-
Тщательно записываются
-
Не прерывают задачу
-
Явные и неявные
-
Использутся на всю катушку!
Сохраняя при этом природу веба!
Оболочка приложения
Давайте спроектируем App Shell
My App
-
Определить ресурсы
-
Закэшировать
-
Выдать из кэша
-
Управлять версиями
}
Сервис-воркер
Логически
Физически
-файл(ы)
Вебсайт
Сервис-воркер
Браузер/ОС
Event-driven worker
Кэш
fetch
push
sync
Предварительное кэширование
self.addEventListener('install', event => {
// Помещаем ресурсы app shell в Cache Storage
})
self.addEventListener('activate', event => {
// Удаляем из Cache Storage устаревшие версии
})
handmade-service-worker.js
Перехватываем запросы
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request)
})
)
})
handmade-service-worker.js
Внезапно...
В реальности...
Редиректы?
Fallback?
Opaque response?
Версионность?
Инвалидация кэша?
Обновление спецификаций?
Размер локального хранилища?
Переменные имена ресурсов?
Feature detection?
Минимально необходимое обновление кэша?
Стратегии кэширования?
Роутинг?
Точные настройки стратегий?
Модульность?
Я вижу старую версию!!!
-
Определить ресурсы
-
Закэшировать
-
Выдать из кэша
-
Управлять версиями
}
Есть ли помощник?
# Будем использовать как модуль Node
$ npm install workbox-build --save-dev
И множество других модулей
Конфигурация
var workboxConfig = {
globDirectory: 'dist/',
globPatterns: [
'**/*.{txt,png,ico,html,js,json,css}'
],
swSrc: 'src/workbox-service-worker.js',
swDest: 'dist/sw.js'
}
workbox-build.js
Исходник сервис-воркера
// Загрузим с Google CDN (но лучше иметь локально)
importScripts('https://googleapis.com/.../workbox-sw.js')
// Все остальное
workbox.precaching.precacheAndRoute([])
src/workbox-service-worker.js

Кэширование, выдача, управление версиями
Билд-скрипт
const {injectManifest} = require('workbox-build')
var workboxConfig = {...}
// Наполняем сервис-воркер информацией о ресурсах
injectManifest(workboxConfig).then(({count, size}) => {
console.log(`Generated ${workboxConfig.swDest},
which will precache ${count} files, ${size} bytes.`)
})
workbox-build.js
Интеграция с билдом приложения
{
"scripts": {
"build-pwa": "npm run build-app &&
node workbox-build.js"
}
}
package.json
Данные приложения
Перехват запросов
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
-
...ваш собственный?
Сохранение офлайн-действий
Background sync
-
Одноразовое событие офлайн->онлайн
-
Работает при закрытом приложении
const swReg = await navigator.serviceWorker.ready
if ('sync' in swReg) {
// Сохраняем данные в IndexedDB и...
swReg.sync.register('postTweet')
}
main.js
self.addEventListener('sync', event => {
if (event.tag == 'postTweet') {
event.waitUntil(
// Извлекаем из IndexedDB и отправляем на сервер
)
}
})
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'
)
Загрузки под контролем
Background fetch
-
Авто-остановка/продолжение загрузок (down/up)
-
Отслеживание состояния и прогресса в коде
-
Работает при закрытом приложении
-
Пользователь управляет и следит за процессом
const swReg = await navigator.serviceWorker.ready
await swReg.backgroundFetch.fetch(
'my-series',
['s01e01.mpg', 's01e02.mpg'],
{
title: 'Первый сезон',
downloadTotal: 600 * 1024 * 1024
}
)
main.js
self.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
Подробнее и с примерами


Планирование обновлений
Periodic background sync
-
Cron в браузере
-
Не зависит от сети
-
Периодичность задается разработчиком
-
Другой API для этого
-
Работает только онлайн
-
Браузер решает
const swReg = await navigator.serviceWorker.ready
if ('periodicSync' in swReg) {
swReg.periodicSync.register('refreshTweets', {
// Минимальный период - 24 часа
minInterval: 24 * 60 * 60 * 1000,
})
}
main.js
self.addEventListener('periodicsync', event => {
if (event.tag === 'refreshTweets') {
event.waitUntil(
// [Может быть] Выполняем полезные действия
)
}
})
src/service-worker.js
-
Тип сетевого соединения?
-
Режим экономии трафика?
-
Доступное место на устройстве?
chrome://flags/#enable-devtools-experim
Отладка фоновых сервисов


А что насчет "другого API"
-
Notification Triggers
-
Scheduled Task
Еще инструментов?

Native File System API

Badging API

Contact Picker API
Готовы к взлету

Почти 100 новых API
-
Полноценная платформа для приложений
-
Механизмы офлайн-готовности
-
Отличный инструментарий
-
Главное — пользовательский опыт
Всё только начинается!
Веб сегодня
-
2000+ участников
-
Представители браузеров, фреймворков, библиотек
-
Все о PWA на русском языке
Спасибо!
@webmaxru
Максим Сальников
Есть вопрос?
@webmaxru
Максим Сальников
Погружение в глубокий офлайн - веб способен на это!
By Maxim Salnikov
Погружение в глубокий офлайн - веб способен на это!
Прогрессивные веб-приложения уже получили действительно широкую известность и признание всеми вовлеченными сторонами: разработчиками браузеров (наконец, всеми!), разработчиками, пользователями. Идея приложений, не зависящих от подключения к сети, доказала свою жизнеспособность, и мы видим все больше и больше проектов, идущих по этому пути, что делает возможность работы в офлайне не только лучшей практикой, но просто и хорошей манерой в вебе. В моем докладе, основанном на глубоком исследовании возможностей Service Worker API (с использованием Cache Storage, Background Fetch, Background Sync) и собранных UX-находках, мы рассмотрим историю офлайн веба, важность рассмотрения подключения как привилегии, текущие проблемы (и их решения) и правильные инструменты. В течение докдада мы спроектируем приложение, готовое к работе офлайн, применяя лучшие технологии и UX-практики и добавляя возможности одна за одной: оболочка приложения, кеширование ресурсов и данных, синхронизация при подключении к сети. Все ради наших пользователей, которые требуют нового уровня отказоустойчивости и скорости работы наших приложений.
- 3,808