Maxim Salnikov
Angular GDE
Azure Developer Technical Lead at Microsoft
Progressive web apps use modern web APIs along with traditional progressive enhancement strategy to create cross-platform web applications.
* but not everything**
** use progressive enhancement strategy
Service Worker API
Web App Manifest
My App
App
Service-worker
Browser/OS
Event-driven worker
Cache
fetch
push
sync
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
    )
  }
})
$ ng add @angular/pwa$ ng build --prod$ ng serve{
    "name": "app",
    "installMode": "prefetch",
    "resources":
}    "resources": {    
        "files": [
            "/favicon.ico",
            "/index.html",
            "/*.css",
            "/*.js"
        ]
    }# 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.`)
})// 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([])
[
  {
    "url": "index.html",
    "revision": "34c45cdf166d266929f6b532a8e3869e"
  },
  {
    "url": "favicon.ico",
    "revision": "b9aa7c338693424aae99599bec875b5f"
  },
  ...
]{
  "scripts": {
    "build-prod": "ng build --prod &&
                   node workbox-build-inject.js"
  }
}{
    "name": "api-freshness",
    "urls": [
      "/api/breakingnews/**"
    ],
}    "cacheConfig": {
      "strategy": "freshness",
      "maxSize": 10,
      "maxAge": "12h",
      "timeout": "10s"
    }{
    "name": "api-performance",
    "urls": [
      "/api/archive/**"
    ],
}    "cacheConfig": {
      "strategy": "performance",
      "maxSize": 100,
      "maxAge": "365d"
    }{
    "version": 1,
    "name": "api-performance",
    "urls": [
      "/api/**"
    ],
    ...
}{
    "version": 2,
    "name": "api-performance",
    "urls": [
      "/api/**"
    ],
    ...
}workbox.routing.registerRoute(
  new RegExp('/app/v2/'),
  workbox.strategies.networkFirst()
);workbox.routing.registerRoute(
  new RegExp('/images/'),
  workbox.strategies.cacheFirst({
    plugins: [...]
  })
);A new version of the app is available. Click to refresh.
import { SwUpdate } from '@angular/service-worker';
constructor(updates: SwUpdate) {}this.updates.available.subscribe(event => {
})    if (confirm(`New Version is available! OK to refresh`)) {
            window.location.reload();
    }{
  "appData": {
    "changelog": "New version: Dinosaur pic was added!"
  }
}let changelog = event.available.appData['changelog']
let message = `${changelog} Click to refresh.`New version: Dinosaur pic was added! Click to refresh.
import { Workbox } from 'workbox-window'
platformBrowserDynamic().bootstrapModule(AppModule)
  .then( () => {
    if ('serviceWorker' in navigator) {
      const wb = new Workbox('service-worker.js');
      // Event listeners...
      wb.register();
    }
  })$ npm install workbox-windowwb.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()Maxim Salnikov
@webmaxru
Maxim Salnikov
@webmaxru