Maxim Salnikov
Angular GDE
How to create Angular Progressive Web App?
Products from the future
UI Engineer at ForgeRock
March 1-2, Helsinki, Finland
... attempts to combine features offered by most modern browsers with the benefits of mobile experience
... web apps that use the latest web technologies.
Progressive
Discoverable
Linkable
App-like
Responsive
Connectivity-independent
Re-engageable
Installable
Fresh
Safe
App
Service worker
My App
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
)
}
})
v1.0.0-beta.16 - Experimental service worker by the Angular Mobile team
# Install the Angular Service Worker
$ npm install --save @angular/service-worker
# Enable the SW registration + app shell in Angular CLI
$ ng set apps.0.serviceWorker=true
{
"static": {...},
"routing": {...},
"external": {...},
"dynamic": {...},
"push": {...}
}
$ ng build --prod
v5.0.0-rc.7
# Install the New Angular Service Worker
$ npm install --save @angular/service-worker@next
# Generate control file in ./dist folder
$ node_modules/.bin/ngsw-config dist ./src/ngsw-config.json
ngsw-config outputFolder configurationFile baseHref
{
"index": "/index.html",
"assetGroups": [...],
"dataGroups": [...]
}
{
"name": "appshell",
"installMode": "prefetch",
"updateMode": "lazy",
"resources": {}
}
"resources": {
"files": [
"/assets/**/*"
],
"versionedFiles": [
"/**/*.html",
"/**/*.js",
"/**/*.css"
],
"urls": [
"https://fonts.googleapis.com/css?family=Material+Icons",
"https://fonts.gstatic.com/s/materialicons/v29/2fcr.woff2"
]
}
{
"name": "api-freshness",
"urls": [
"/breakingnews"
],
"cacheConfig": {
"strategy": "freshness",
"maxSize": 10,
"maxAge": "12h",
"timeout": "1m"
}
}
{
"name": "api-performance",
"urls": [
"/archive"
],
"cacheConfig": {
"strategy": "performance",
"maxSize": 100,
"maxAge": "365d"
}
}
{
"configVersion": 1,
"index": "/index.html",
"assetGroups": [...],
"dataGroups": [...],
"hashTable": {
"/assets/favicon-16x16.png": "a153ae82ddbafe...",
"/assets/favicon-32x32.png": "5e6c0c6faee615...",
"/assets/images/logo.png": "90a19d7780c0...",
...
}
}
“ng build -prod && node_modules/.bin/ngsw-config dist ./src/ngsw-config.json && cp node_modules/@angular/service-worker/ngsw-worker.js ./dist/ngsw-worker.js”
“build-prod-ngsw”:
import { ServiceWorkerModule } from '@angular/service-worker'
...
@NgModule({
imports: [
...
ServiceWorkerModule.register('/ngsw-worker.js')
]
...
})
export class AppModule { }
# Generates ngsw-config.json with smart defaults
# And imports ServiceWorkerModule.register()
$ ng new --service-worker
# Builds an app, generates ngsw.json, copies ngsw-worker.js
$ ng build --prod
# Install the Workbox CLI
$ npm install workbox-cli --global
# Generate a service worker with some smart defaults
$ workbox generate:sw
module.exports = {
"globDirectory": "dist/",
"globPatterns": [
"**/*.{txt,png,ico,html,js,json,css}"
],
"swDest": "dist/sw-default.js",
"globIgnores": [
"3rdpartylicenses.txt"
]
};
importScripts('workbox-sw.prod.v2.0.0.js');
const fileManifest = [
{
"url": "index.html",
"revision": "ab950af06a80f755cd4bc1e34b3d6641"
},
...
];
const workboxSW = new self.WorkboxSW();
workboxSW.precache(fileManifest);
platformBrowserDynamic()
.bootstrapModule(AppModule)
.then(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw-default.js')
}
});
"assets": [
{
"glob": "workbox-sw.dev.v2.0.0.js",
"input": "../node_modules/workbox-sw/build/...",
"output": "./"
},
...
]
$ npm install --save workbox-sw
$ npm install --save-dev workbox-build
importScripts('./workbox-sw.dev.v2.0.0.js')
const workboxSW = new WorkboxSW()
workboxSW.precache([])
const apiStrategy = workboxSW.strategies.networkFirst()
workboxSW.router.registerRoute(
/(http[s]?:\/\/)?([^\/\s]+\/)(api)/,
apiStrategy
)
const swBuild = require('workbox-build')
swBuild
.injectManifest({
globDirectory: 'dist/',
globPatterns: [
'**/*.{txt,png,ico,html,js,json,css}'
],
globIgnores: ['3rdpartylicenses.txt'],
swSrc: './src/my-serviceworker.js',
swDest: './dist/my-serviceworker.js'
})
$ npm install --save workbox-routing
$ npm install --save workbox-runtime-caching
$ npm install --save workbox-background-sync
importScripts('./workbox-routing.dev.v2.0.0.js')
importScripts('./workbox-runtime-caching.dev.v2.0.0.js')
importScripts('./workbox-background-sync.dev.v2.0.0.js')
"assets": [
...
]
backgroundSync.QueuePlugin
runtimeCaching.RequestWrapper
runtimeCaching.NetworkOnly
routing.RegExpRoute
routing.Router
workboxSW
Flexibility
Automation
Stability