1 از 45

Progressive Web Apps

Reliable

2 از 45

Going Offline

3 از 45

How could an in-browser proxy help make your PWA work offline?

4 از 45

Going Offline - The Key Players

Service Workers

  • In-browser proxy
  • Works online or offline

Cache Storage API

  • Manage browser’s site cache
  • Add/remove/delete/check for cache items

5 از 45

Life of a Proxied Request

Browser

Server

Service Worker

Browser Cache

6 از 45

Service Worker Scope - https://example.com/app/sw.js

URL

In-Scope

Reason

https://example.com/app

Yes

Matches root of scope

https://example.com/app/css/style.css

Yes

Is beneath root of scope

https://example.com/other-app

No

Above root of scope

https://example.com/js/shared.js

No

Above root of scope

7 از 45

8 از 45

Service Worker Update - https://example.com/app/sw.js

URL

Content Changed

Update

Reason

https://example.com/app/sw.js

No

No

No content changed,�same service worker path

https://example.com/app/sw.js

Yes

Yes

Content changed,�same service worker path

https://example.com/app/sw.12345.js

Yes

No

Different service worker path

https://example.com/js/sw.js

No

No

Different service worker path

https://example.com/js/sw.js

Yes

No

Different service worker path

9 از 45

10 از 45

Service Workers &�Cache Storage API

Debugging with DevTools

11 از 45

Application Panel - Service Workers

12 از 45

Application Panel - Service Workers

13 از 45

Application Panel - Service Workers

14 از 45

Application Panel - Service Workers

15 از 45

Try It Out - 15 Minutes

https://workshops.page.link/pwa03--going-offline

16 از 45

Caching Strategies

17 از 45

How might you cache frequently updated content? Infrequently updated?

18 از 45

Caching Strategy - Stale-While-Revalidate

19 از 45

Caching Strategy - Network First

20 از 45

Caching Strategy - Cache First

21 از 45

Caching Strategy - Cache Only

22 از 45

Caching Strategy - Network Only

23 از 45

Choosing a Strategy

  • How frequently is this updated?
  • Does this depend on something else?

24 از 45

Working with Workbox

25 از 45

Workbox - Setting Up Routing and Caching

import { registerRoute } from 'workbox-routing';

import { NetworkFirst } from 'workbox-strategies';

import { CacheableResponsePlugin } from 'workbox-cacheable-response';

26 از 45

Workbox - Setting Up Routing and Caching

const pageStrategy = new NetworkFirst({

// Put all cached files in a cache named 'pages'

cacheName: 'pages',

plugins: [

// Ensure that only requests that result in a 200 status are cached

new CacheableResponsePlugin({

statuses: [200],

}),

],

});

27 از 45

Workbox - Setting Up Routing and Caching

// Cache page navigations (html) with a Network First strategy

registerRoute(

// Check to see if the request is a navigation to a new page

({ request }) => request.mode === 'navigate',

// Use the strategy

pageStrategy

);

28 از 45

Workbox - Offline Fallback

import { setCatchHandler } from 'workbox-routing';

// Warm the cache when the Service Worker installs

self.addEventListener('install', event => {

const files = ['/offline.html'];

event.waitUntil(

self.caches.open('offline-fallbacks')

.then(cache => cache.addAll(files))

);

});

29 از 45

Workbox - Offline Fallback

// Respond with the fallback if a route throws an error

setCatchHandler(async (options) => {

const dest = options.request.destination;

const cache = await self.caches.open('offline-fallbacks');

if (dest === 'document') {

return (await cache.match('/offline.html')) || Response.error();

}

return Response.error();

});

30 از 45

Workbox Recipes

import {

pageCache,

googleFontsCache,

offlineFallback,

} from 'workbox-recipes';

pageCache();

googleFontsCache();

offlineFallback({

pageFallback: '/offline.html'

});

31 از 45

Try It Out - 15 Minutes

https://workshops.page.link/pwa03--workbox

32 از 45

IndexedDB

33 از 45

How do you think IndexedDB differs from Cache Storage?

34 از 45

Cache Storage API

IndexedDB

  • Network resources
  • File-based content
  • Structured data
  • Searchable data

35 از 45

Opening a Database

// Using https://github.com/jakearchibald/idb

import { openDB } from 'idb';

const db = openDB('bands', 1, {

upgrade(db, oldVersion, newVersion, transaction) {

// Switch over the oldVersion to allow the database to be incrementally upgraded. No `break` so all of the updates get run!

36 از 45

Opening a Database

switch(oldVersion) {

case: 0

// Placeholder to execute when database is first created (oldVersion is 0)

case 1:

// Create a store of objects

const store = db.createObjectStore('beatles', {

// The `name` property of the object will be the key.

keyPath: 'name'

});

// Create an store index called `age` based on the `age` key of objects in the store

store.createIndex('age', 'age');

}

}

});

37 از 45

The “Beatles” Object Store

#

Key (Key path: “name”)

Value

0

John Lennon

{name: 'John Lennon', nickname: 'The smart one', age: 40, living: false}

1

Paul McCartney

{name: 'Paul McCartney', nickname: 'The cute one', age: 73, living: true}

2

Ringo Starr

{name: 'Ringo Starr', nickname: 'The funny one', age: 74, living: true}

38 از 45

Getting an Item

// Using https://github.com/jakearchibald/idb

const tx = await db.transaction('beatles', 'read')

const store = tx.objectStore('beatles');

const value = await store.get('John Lennon');

39 از 45

Getting an Item

// Using https://github.com/jakearchibald/idb

const george = {

name: 'George Harrison',

nickname: 'The shy one',

age: 58,

living: false

};

const tx = await db.transaction('beatles', 'readwrite');

const store = tx.objectStore('beatles');

store.add(george);

await tx.done

40 از 45

The “Beatles” Object Store

#

Key (Key path: “name”)

Value

0

John Lennon

{name: 'John Lennon', nickname: 'The smart one', age: 40, living: false}

1

Paul McCartney

{name: 'Paul McCartney', nickname: 'The cute one', age: 73, living: true}

2

Ringo Starr

{name: 'Ringo Starr', nickname: 'The funny one', age: 74, living: true}

3

George Harrison

{name: 'George Harrison', nickname: 'The shy one', age: 58, living: false}

41 از 45

Managing Storage

42 از 45

Available Storage

if (navigator.storage && navigator.storage.estimate) {

const quota = await navigator.storage.estimate();

// quota.usage -> Number of bytes used.

// quota.quota -> Maximum number of bytes available.

const percentageUsed = (quota.usage / quota.quota) * 100;

console.log(`You've used ${percentageUsed}% of the available storage.`);

const remaining = quota.quota - quota.usage;

console.log(`You can write up to ${remaining} more bytes.`);

}

43 از 45

Storage in the Applications Panel

44 از 45

Try It Out - 15 Minutes

https://workshops.page.link/pwa03--indexeddb

45 از 45

await tx.done;