Maxim Salnikov
@webmaxru
Maxim Salnikov
@webmaxru
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
Recommended to copy code snippets from
Optional
Full-Stack Developer, PWAdvocate
Progressive web apps use modern web APIs along with traditional progressive enhancement strategy to create cross-platform web applications.
Service Worker API
Web App Manifest
Service Worker API
install, activate, fetch, backgroundfetchsuccess, backgroundfetchfail, backgroundfetchclick
sync
push, notificationclick
paymentrequest
Flagged
OS
* but not everything**
** use progressive enhancement strategy
Web API Confluence
Not available in service worker:
My App
Define the set of assets required to show the minimum viable UI
Service worker
install: put the assets into Cache Storage
activate: clear Cache Storage from the previous app version assets
fetch: if the asset is in Cache Storage serve it from there. Otherwise — download and serve it (and cache it)
Build time
Register service worker the way it does not affect the app loading performance
Website/webapp
Website
Service-worker
Browser/OS
Event-driven worker
'install'
Parsed
Installing
Activating
Redundant
'activate'
Waiting
Active
self.addEventListener('install', (event) => {
// Put app's html/js/css to cache
})
self.addEventListener('activate', (event) => {
// Wipe previous version of app files from cache
})
self.addEventListener('fetch', (event) => {
if (event.request.url.indexOf('/api') != -1) {
event.respondWith(
// Network-First Strategy
)
} else {
event.respondWith(
// Cache-First Strategy
)
}
})
1) What are the cons and challenges of the manually written service workers
2) Introducing Workbox
3) Automating application shell lifecycle: from building to serving
4) Organizing a proper UX for the app update
5) Runtime caching: strategies
6) What is background sync and how to implement it using Workbox
7) Proper registration of Workbox in your app
Maxim Salnikov
@webmaxru
Maxim Salnikov
@webmaxru
# Installing the Workbox Node module
$ 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.`)
})
[
{
"url": "index.html",
"revision": "34c45cdf166d266929f6b532a8e3869e"
},
{
"url": "favicon.ico",
"revision": "b9aa7c338693424aae99599bec875b5f"
},
...
]
// Sample configuration with the basic options
var workboxConfig = {
globDirectory: 'dist/angular-pwa/',
globPatterns: [
'**/*.{txt,png,ico,html,js,json,css}'
],
swSrc: 'src/service-worker.js',
swDest: 'dist/angular-pwa/service-worker.js'
}
// Importing Workbox itself from Google CDN
importScripts('https://googleapis.com/workbox-sw.js');
// Precaching and setting up the routing
workbox.precaching.precacheAndRoute([])
{
"scripts": {
"build-prod": "ng build --prod &&
node workbox-build-inject.js"
}
}
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')
]);
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/service-worker.js')
}
import { register } from 'register-service-worker'
platformBrowserDynamic().bootstrapModule(AppModule)
.then( () => {
register('/service-worker.js', {
})
})
$ npm install register-service-worker
updated (registration) {
// Inform & prompt
}
workbox.routing.registerRoute(
new RegExp('/app/v2/'),
workbox.strategies.networkFirst()
);
workbox.routing.registerRoute(
new RegExp('/images/'),
workbox.strategies.cacheFirst({
plugins: [...]
})
);
self.addEventListener('push', (event) => {
self.registration.showNotification(...)
})
self.addEventListener('notificationclick', (event) => {
// React on notification actions
})
self.addEventListener('notificationclose', (event) => {
// React on notification closing
})