Максим Сальников
@webmaxru
Что значит готовность веб-приложения к офлайн
Ответственный за успех Azure-разработчиков в Microsoft
Браузеры
JavaScript
Браузеры
JavaScript
Движки JS
Браузеры
JavaScript
Движки JS
Интерфейсы
Браузеры
Сообщество
JavaScript
Движки JS
Интерфейсы
Браузеры
Сообщество
JavaScript
Движки JS
Интерфейсы
Браузеры
Проблемы?
(по историческим причинам)
Такие приложения запускаются везде и обладают рядом характеристик, обеспечивающих пользователей преимуществами, аналогичными тем, что доступны в нативных решениях.
* но не все возможности доступны везде**
** применяем стратегию прогрессивного улучшения
My App
Вебсайт
Сервис-воркер
Браузер/ОС
Event-driven worker
Кэш
fetch
push
sync
self.addEventListener('install', event => {
// Помещаем ресурсы app shell в Cache Storage
})
self.addEventListener('activate', event => {
// Удаляем из Cache Storage устаревшие версии
})
self.addEventListener('fetch', event => {
if (event.request.url.indexOf('/api') != -1) {
event.respondWith(
// Реализуем стратегию Network-First (для API?)
)
} else {
event.respondWith(
// Реализуем стратегию Cache-First (для app shell?)
)
}
})
# Устанавливаем модуль 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} байт.`)
})
var workboxConfig = {
globDirectory: 'dist/',
globPatterns: [
'**/*.{txt,png,ico,html,js,json,css}'
],
swSrc: 'src/workbox-service-worker.js',
swDest: 'dist/sw.js'
}
// Импортируем библиотеку Workbox с Google CDN
importScripts('https://googleapis.com/.../workbox-sw.js');
// Все остальное делается за нас
workbox.precaching.precacheAndRoute([])
[
{
"url": "index.html",
"revision": "34c45cdf166d266929f6b532a8e3869e"
},
{
"url": "favicon.ico",
"revision": "b9aa7c338693424aae99599bec875b5f"
},
...
]
{
"scripts": {
"build-pwa": "npm run build-app &&
node workbox-build-inject.js"
}
}
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
)
}
})
workbox.routing.registerRoute(
new RegExp('/api/breakingnews'),
new workbox.strategies.NetworkFirst()
);
workbox.routing.registerRoute(
new RegExp('/api/archive'),
new workbox.strategies.CacheFirst({
plugins: [...]
})
);
navigator.serviceWorker.ready.then( swRegistration => {
return swRegistration.sync.register('myFirstSync');
});
self.addEventListener('sync', event => {
if (event.tag == 'myFirstSync') {
event.waitUntil(
// То, что было запланировано
);
}
});
const postTweetPlugin =
new workbox.backgroundSync.Plugin('tweetsQueue', {
maxRetentionTime: 24 * 60 // Лимит времени
})
workbox.routing.registerRoute(
/(http[s]?:\/\/)?([^\/\s]+\/)post-tweet/,
new workbox.strategies.NetworkOnly({
plugins: [postTweetPlugin]
}),
'POST'
)
const registration = await navigator.serviceWorker.ready;
await registration.backgroundFetch.fetch(
'my-series',
['s01e01.mpg', 's01e02.mpg'],
{
title: 'Загрузка My Series',
downloadTotal: 1000000000
}
);
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}` });
}
})()
);
});
"Progressive Web Apps State of the Union" от Dominick Ng на BlinkOn 10