Going Offline

A presentation at An Event Apart San Francisco 2019 in December 2019 in San Francisco, CA, USA by Jeremy Keith

Slide 1

Slide 1

Going Offline

Slide 2

Slide 2

adactio.com

Slide 3

Slide 3

adactio.com

Slide 4

Slide 4

resilientwebdesign.com

Slide 5

Slide 5

2005

Slide 6

Slide 6

JavaScript When someone mouses over a link, make it look different. When someone fills out a form, make sure the inputs are valid.

Slide 7

Slide 7

JavaScript Find stuff and do stuff to it.

Slide 8

Slide 8

JavaScript Find stuff and do stuff to it. jQuery

Slide 9

Slide 9

JavaScript Find stuff and do stuff to it. $(‘.stuff’).click(doStuff); jQuery

Slide 10

Slide 10

JavaScript document.querySelector(‘.stuff’) .addEventListener(‘click’, doStuff); Find stuff and do stuff to it. $(‘.stuff’).click(doStuff); jQuery

Slide 11

Slide 11

JavaScript code Logic.

Slide 12

Slide 12

JavaScript Logic. code

Slide 13

Slide 13

2007

Slide 14

Slide 14

Update part of this page with data from a server. Ajax

Slide 15

Slide 15

DOM Scripting Find stuff on this page and do stuff to it. Update part of this page with data from a server. Ajax

Slide 16

Slide 16

DOM Scripting Find stuff on this page and do stuff to it. Update part of this page with data from a server. Ajax

Slide 17

Slide 17

DOM Scripting web worker Ajax

Slide 18

Slide 18

web worker

Slide 19

Slide 19

service worker

Slide 20

Slide 20

2018

Slide 21

Slide 21

example.com

Slide 22

Slide 22

example.com

Slide 23

Slide 23

example.com

Slide 24

Slide 24

example.com

Slide 25

Slide 25

example.com

Slide 26

Slide 26

example.com

Slide 27

Slide 27

page.html styles.css script.js icon.png example.com

Slide 28

Slide 28

serviceworker.js example.com

Slide 29

Slide 29

serviceworker.js example.com

Slide 30

Slide 30

serviceworker.js example.com

Slide 31

Slide 31

serviceworker.js example.com

Slide 32

Slide 32

serviceworker.js example.com

Slide 33

Slide 33

serviceworker.js example.com

Slide 34

Slide 34

serviceworker.js example.com

Slide 35

Slide 35

serviceworker.js https ://example.com

Slide 36

Slide 36

https

Slide 37

Slide 37

register install active update life cycle

Slide 38

Slide 38

register

Slide 39

Slide 39

install

Slide 40

Slide 40

active

Slide 41

Slide 41

update

Slide 42

Slide 42

update

Slide 43

Slide 43

active

Slide 44

Slide 44

register

Slide 45

Slide 45

register Get the JavaScript file ‘serviceworker.js’

Slide 46

Slide 46

register <script src=”/serviceworker.js”> </script>

Slide 47

Slide 47

register <script src=”/serviceworker.js”> </script>

Slide 48

Slide 48

register <script> navigator.serviceWorker.register(); </script>

Slide 49

Slide 49

register <script> navigator.serviceWorker.register (‘/serviceworker.js’); </script>

Slide 50

Slide 50

register <script> navigator.serviceWorker.register (‘/assets/js/serviceworker.js’); </script>

Slide 51

Slide 51

register <script> navigator.serviceWorker.register (‘/assets/js/serviceworker.js’); </script>

Slide 52

Slide 52

register <script> navigator.serviceWorker.register (‘/serviceworker.js’); </script>

Slide 53

Slide 53

register <script> if (navigator.serviceWorker) { navigator.serviceWorker.register (‘/serviceworker.js’); } </script>

Slide 54

Slide 54

register Get the JavaScript file ‘serviceworker.js’

<script> if (navigator.serviceWorker) { navigator.serviceWorker.register (‘/serviceworker.js’); } </script>

Slide 55

Slide 55

register Install the JavaScript file ‘serviceworker.js’

<script> if (navigator.serviceWorker) { navigator.serviceWorker.register (‘/serviceworker.js’); } </script>

Slide 56

Slide 56

install

Slide 57

Slide 57

install caching

Slide 58

Slide 58

memory cache browser cache Cache API caching

Slide 59

Slide 59

install precaching

Slide 60

Slide 60

install styles.css script.js icon.png

Slide 61

Slide 61

install styles.css script.js icon.png

Slide 62

Slide 62

install styles.css script.js icon.png

Slide 63

Slide 63

install styles.css script.js icon.png

Slide 64

Slide 64

install styles.css script.js icon.png

Slide 65

Slide 65

install styles.css script.js icon.png

Slide 66

Slide 66

install script.js styles.css icon.png

Slide 67

Slide 67

install

Slide 68

Slide 68

install precaching

Slide 69

Slide 69

precaching When this service worker is installing open a cache and then put a bunch of files in that cache.

Slide 70

Slide 70

precaching When this service worker is installing

Slide 71

Slide 71

precaching When this service worker is installing addEventListener()

Slide 72

Slide 72

precaching When this service worker is installing addEventListener(‘install’)

Slide 73

Slide 73

precaching When this service worker is installing addEventListener(‘install’, function(event) { });

Slide 74

Slide 74

precaching When this service worker is installing addEventListener(‘install’, event => { });

Slide 75

Slide 75

precaching open a cache

Slide 76

Slide 76

precaching open a cache caches.open()

Slide 77

Slide 77

precaching open a cache caches.open(‘files’)

Slide 78

Slide 78

precaching open a cache and then caches.open(‘files’)

Slide 79

Slide 79

precaching open a cache and then caches.open(‘files’).then()

Slide 80

Slide 80

precaching open a cache and then caches.open(‘files’).then( cache => { });

Slide 81

Slide 81

precaching put a bunch of files in that cache.

Slide 82

Slide 82

precaching put a bunch of files in that cache. cache.addAll()

Slide 83

Slide 83

precaching put a bunch of files in that cache. cache.addAll([ ‘/css/styles.css’, ‘/js/script.js’, ‘/img/icon.png’ ]);

Slide 84

Slide 84

precaching

Slide 85

Slide 85

precaching When this service worker is installing addEventListener(‘install’, event => { });

Slide 86

Slide 86

precaching When this service worker is installing open a cache caches.open(‘files’)

Slide 87

Slide 87

precaching When this service worker is installing open a cache and then caches.open(‘files’).then( cache => { });

Slide 88

Slide 88

precaching When this service worker is installing open a cache and then put a bunch of files in that cache. cache.addAll([ ‘/css/styles.css’, ‘/js/script.js’, ‘/img/icon.png’ ]);

Slide 89

Slide 89

precaching When this service worker is installing open a cache and then put a bunch of files in that cache.

Slide 90

Slide 90

precaching addEventListener(‘install’, event => { });

Slide 91

Slide 91

precaching addEventListener(‘install’, event => { caches.open(‘files’) });

Slide 92

Slide 92

precaching addEventListener(‘install’, event => { caches.open(‘files’).then( cache => { }); });

Slide 93

Slide 93

precaching addEventListener(‘install’, event => { caches.open(‘files’).then( cache => { cache.addAll([ ‘/css/styles.css’, ‘/js/script.js’, ‘/img/icon.png’ ]); }); });

Slide 94

Slide 94

precaching addEventListener(‘install’, event => { caches.open(‘files’).then( cache => { cache.addAll([ ‘/css/styles.css’, ‘/js/script.js’, ‘/img/icon.png’ ]); }); });

Slide 95

Slide 95

install

Slide 96

Slide 96

active

Slide 97

Slide 97

cache first When a file is requested try to find the file in the cache first otherwise fetch the file from the network.

Slide 98

Slide 98

cache first When a file is requested

Slide 99

Slide 99

cache first When a file is requested addEventListener()

Slide 100

Slide 100

cache first When a file is requested addEventListener(‘fetch’)

Slide 101

Slide 101

cache first When a file is requested addEventListener(‘fetch’, event => { });

Slide 102

Slide 102

cache first When a file is requested addEventListener(‘fetch’, event => { event.respondWith( ); });

Slide 103

Slide 103

cache first try to find the file in the cache first

Slide 104

Slide 104

cache first try to find the file in the cache first caches.match(event.request)

Slide 105

Slide 105

cache first try to find the file in the cache first caches.match(event.request) .then( response => { })

Slide 106

Slide 106

cache first try to find the file in the cache first caches.match(event.request) .then( response => { return response; })

Slide 107

Slide 107

cache first try to find the file in the cache first otherwise fetch the file from the network. caches.match(event.request) .then( response => { return response; })

Slide 108

Slide 108

cache first try to find the file in the cache first otherwise fetch the file from the network. caches.match(event.request) .then( response => { return response || fetch(event.request); })

Slide 109

Slide 109

cache first When a file is requested try to find the file in the cache first otherwise fetch the file from the network.

Slide 110

Slide 110

cache first addEventListener(‘fetch’, event => { event.respondWith( ); });

Slide 111

Slide 111

cache first addEventListener(‘fetch’, event => { event.respondWith( caches.match(event.request) .then( response => { return response }) ); });

Slide 112

Slide 112

cache first addEventListener(‘fetch’, event => { event.respondWith( caches.match(event.request) .then( response => { return response || fetch(event.request); }) ); });

Slide 113

Slide 113

cache first addEventListener(‘fetch’, event => { event.respondWith( caches.match(event.request) .then( response => { return response || fetch(event.request); }) ); });

Slide 114

Slide 114

cache first

Slide 115

Slide 115

offline first

Slide 116

Slide 116

offline first

Slide 117

Slide 117

offline first

Slide 118

Slide 118

offline first

Slide 119

Slide 119

offline first

Slide 120

Slide 120

offline first

Slide 121

Slide 121

offline first

Slide 122

Slide 122

offline first

Slide 123

Slide 123

offline first precaching cache first install active

Slide 124

Slide 124

css js img precaching cache first

Slide 125

Slide 125

html network first custom offline page

Slide 126

Slide 126

network first

Slide 127

Slide 127

precaching addEventListener(‘install’, event => { caches.open(‘files’).then( cache => { cache.addAll([ ‘/css/styles.css’, ‘/js/script.js’, ‘/img/icon.png’ ]); }); });

Slide 128

Slide 128

precaching addEventListener(‘install’, event => { caches.open(‘files’).then( cache => { cache.addAll([ ‘/css/styles.css’, ‘/js/script.js’, ‘/img/icon.png’, ‘/offline.html’ ]); }); });

Slide 129

Slide 129

network first When a page is requested try to fetch the page from the network first otherwise retrieve the offline page from the cache.

Slide 130

Slide 130

network first When a page is requested addEventListener(‘fetch’, event => { });

Slide 131

Slide 131

network first When a page is requested addEventListener(‘fetch’, event => { if (event.request.headers .get(‘Accept’).includes(‘text/html’) { } });

Slide 132

Slide 132

network first try to fetch the page from the network first event.respondWith( fetch(event.request) .then( response => { return response; }) );

Slide 133

Slide 133

network first otherwise retrieve the offline page from the cache. .catch( error => { return caches.match(‘/offline.html’); })

Slide 134

Slide 134

network first

Slide 135

Slide 135

network first addEventListener(‘fetch’, event => { if (event.request.headers.get(‘Accept’).includes(‘text/html’) { } });

Slide 136

Slide 136

network first addEventListener(‘fetch’, event => { if (event.request.headers.get(‘Accept’).includes(‘text/html’) { event.respondWith( fetch(event.request) .then( response => { return response; }) } }); );

Slide 137

Slide 137

network first addEventListener(‘fetch’, event => { if (event.request.headers.get(‘Accept’).includes(‘text/html’) { event.respondWith( fetch(event.request) .then( response => { return response; }) .catch( error => { return caches.match(‘offline.html’); }) ); } });

Slide 138

Slide 138

network first addEventListener(‘fetch’, event => { if (event.request.headers.get(‘Accept’).includes(‘text/html’) { event.respondWith( fetch(event.request) .then( response => { return response; }) .catch( error => { return caches.match(‘offline.html’); }) ); } });

Slide 139

Slide 139

Slide 140

Slide 140

html network first custom offline page

Slide 141

Slide 141

html network first cache as you go custom offline page

Slide 142

Slide 142

html network cache custom offline page

Slide 143

Slide 143

cache as you go When a page is requested try to fetch the page from the network first and store a copy in the cache otherwise try to retrieve the page from the cache otherwise retrieve the offline page from the cache.

Slide 144

Slide 144

Slide 145

Slide 145

Slide 146

Slide 146

Slide 147

Slide 147

Slide 148

Slide 148

Slide 149

Slide 149

Slide 150

Slide 150

Slide 151

Slide 151

Slide 152

Slide 152

Slide 153

Slide 153

custom offline page offline.html

Slide 154

Slide 154

Slide 155

Slide 155

css js img html cache network network cache fallback

Slide 156

Slide 156

css js img html speed freshness

Slide 157

Slide 157

articles shopping travel games reference

Slide 158

Slide 158

cache on demand articles

Slide 159

Slide 159

cache on demand When the user presses a button put a copy of this page in the cache.

Slide 160

Slide 160

cache on demand page.html

Slide 161

Slide 161

una.im

Slide 162

Slide 162

una.im

Slide 163

Slide 163

una.im

Slide 164

Slide 164

sarasoueidan.com

Slide 165

Slide 165

sarasoueidan.com

Slide 166

Slide 166

archive.dconstruct.org

Slide 167

Slide 167

archive.dconstruct.org

Slide 168

Slide 168

archive.dconstruct.org

Slide 169

Slide 169

archive.dconstruct.org

Slide 170

Slide 170

web app manifest https service worker

Slide 171

Slide 171

web app manifest <link rel=”manifest” href=”/manifest.json”>

Slide 172

Slide 172

web app manifest { } name: “Resilient Web Design”, short_name: “Resilience”, display: “standalone”, start_url: “/”, theme_color: “#5f7995”, icons: { { src: “/icon.png”, sizes: “512x512”, type: “image/png” } }

Slide 173

Slide 173

Slide 174

Slide 174

Slide 175

Slide 175

Slide 176

Slide 176

Slide 177

Slide 177

Slide 178

Slide 178

Slide 179

Slide 179

Slide 180

Slide 180

Slide 181

Slide 181

Slide 182

Slide 182

Slide 183

Slide 183

web app manifest https service worker progressive web app

Slide 184

Slide 184

“ I have a really hard time describing what a progressive web app actually is.” —What the heck is a “Progressive Web App”? Seriously.

Slide 185

Slide 185

“ A progressive web app is an enhanced version of a Single Page App (SPA) with a native app feel.” —Building Progressive Web Apps

Slide 186

Slide 186

“ Before You Build a PWA You Need a SPA” —hackernoon.com

Slide 187

Slide 187

“ I’ve seen too many devs assume PWA is a subset of SPA. We need to improve our messaging” —Jake Archibald

Slide 188

Slide 188

“ A Progressive Web App is a regular website following a progressive enhancement strategy to deliver native-like user experiences by using modern Web standards.” —What is a PWA

Slide 189

Slide 189

“ The name isn’t for you and worrying about it is distraction from just building things that work better for everyone. … It’s marketing, just like HTML5 had very little to do with actual HTML.” —Naming Progressive Web Apps

Slide 190

Slide 190

Site is served over HTTPS All app URLs load while offline Metadata provided for Add to Home screen Pages are responsive First load fast even on 3G Site works cross-browser Each page has a URL Page transitions don’t block on the network

Slide 191

Slide 191

Site is served over HTTPS All app URLs load while offline Metadata provided for Add to Home screen Pages are responsive First load fast even on 3G Site works cross-browser Each page has a URL Page transitions don’t block on the network

Slide 192

Slide 192

HTTPS offline Metadata

Slide 193

Slide 193

HTTPS offline Metadata HTTPS + service worker + web app manifest = progressive web app

Slide 194

Slide 194

progressive

Slide 195

Slide 195

progressive

Slide 196

Slide 196

Thank you