Maxim Salnikov
@webmaxru
Ответственный за успех Azure-разработчиков в Microsoft
Такие приложения запускаются везде и обладают рядом характеристик, обеспечивающих пользователей преимуществами, аналогичными тем, что доступны в нативных решениях.
* но не все возможности доступны везде**
** применяем стратегию прогрессивного улучшения
Вебсайт
Сервис-воркер
Браузер/ОС
Event-driven worker
Кэш
fetch
push
sync
My App
npm install serve -g
git clone https://github.com/webmaxru/pwa-for-production
cd pwa-for-production
git checkout part-1-init
npm install
serve
Итоговое приложение (можно копировать примеры кода оттуда):
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
// We will use injectManifest mode
const {injectManifest} = require('workbox-build')
// Sample configuration with the basic options
var workboxConfig = {...}
// Calling the method and output the result
injectManifest(workboxConfig).then(({count, size}) => {
console.log(`Generated ${workboxConfig.swDest},
which will precache ${count} files, ${size} bytes.`)
})
// Sample configuration with the basic options
var workboxConfig = {
globDirectory: 'dist/',
globPatterns: [
'**/*.{txt,png,ico,html,js,json,css}'
],
swSrc: 'src/workbox-service-worker.js',
swDest: 'dist/sw.js'
}
// Importing Workbox itself from Google CDN
importScripts('https://googleapis.com/.../workbox-sw.js');
// Precaching and setting up the routing
workbox.precaching.precacheAndRoute([])
Caching, serving, managing versions
[
{
"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 Strategy
)
} else if (event.request.url.indexOf('/api/archive') != -1 {
event.respondWith(
// Cache-First Strategy
)
}
})
workbox.routing.registerRoute(
new RegExp('/api/breakingnews'),
new workbox.strategies.NetworkFirst()
);
workbox.routing.registerRoute(
new RegExp('/api/archive'),
new workbox.strategies.CacheFirst({
plugins: [...]
})
);
A new version of the app is available. Click to refresh.
const updateChannel = new BroadcastChannel('app-shell');
updateChannel.addEventListener('message', event => {
// Inform about the new version & prompt to reload
});
workbox.precaching.addPlugins([
new workbox.broadcastUpdate.Plugin('app-shell')
]);
// Feature detection
if ('serviceWorker' in navigator) {
// Postponing the registration for better performance
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js');
});
}
import { Workbox } from 'workbox-window';
if ('serviceWorker' in navigator) {
const wb = new Workbox('service-worker.js');
// Event listeners...
wb.register();
}
$ npm install workbox-window
<script type="module">
import {Workbox} from 'https://...cdn.com/workbox-window.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('service-worker.js');
wb.register();
}
</script>
wb.addEventListener('installed', event => {
if (event.isUpdate) {
// Show "Newer version is available. Refresh?" prompt
} else {
// Optionally: show "The app is offline-ready" toast
}
});
workbox.core.skipWaiting()
workbox.core.clientsClaim()
wb.addEventListener('activated', (event) => {
// Service worker activated
});
wb.addEventListener('waiting', (event) => {
// Service worker was installed but can't be activated
});
externalinstalled, externalwaiting, externalactivated
Maxim Salnikov
@webmaxru
Maxim Salnikov
@webmaxru