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