Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Building PWAs using Workbox
Search
Arnelle Balane
August 01, 2020
Programming
0
200
Building PWAs using Workbox
Arnelle Balane
August 01, 2020
Tweet
Share
More Decks by Arnelle Balane
See All by Arnelle Balane
Introduction to building Chrome Extensions
arnellebalane
0
80
Color Palettes Of The Most Colorful Birds
arnellebalane
0
95
Let's build a video streaming app using Web technologies
arnellebalane
0
110
Let's build a video calling app with Web technologies and Firebase!
arnellebalane
0
120
Ridiculous Scientific Names
arnellebalane
0
150
Fishes With Terrestrial-Animal Names
arnellebalane
0
130
Making the Web more capable with Project Fugu
arnellebalane
0
94
Frontend Web Development in 2021+
arnellebalane
0
140
Extending CSS using Houdini
arnellebalane
0
91
Other Decks in Programming
See All in Programming
Hotwire or React? ~アフタートーク・本編に含めなかった話~ / Hotwire or React? after talk
harunatsujita
1
120
3rd party scriptでもReactを使いたい! Preact + Reactのハイブリッド開発
righttouch
PRO
1
600
Ethereum_.pdf
nekomatu
0
460
アジャイルを支えるテストアーキテクチャ設計/Test Architecting for Agile
goyoki
9
3.3k
みんなでプロポーザルを書いてみた
yuriko1211
0
260
AWS IaCの注目アップデート 2024年10月版
konokenj
3
3.3k
TypeScript Graph でコードレビューの心理的障壁を乗り越える
ysk8hori
2
1.1k
subpath importsで始めるモック生活
10tera
0
310
CSC509 Lecture 09
javiergs
PRO
0
140
Jakarta EE meets AI
ivargrimstad
0
570
macOS でできる リアルタイム動画像処理
biacco42
9
2.4k
どうして僕の作ったクラスが手続き型と言われなきゃいけないんですか
akikogoto
1
120
Featured
See All Featured
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
Testing 201, or: Great Expectations
jmmastey
38
7.1k
Adopting Sorbet at Scale
ufuk
73
9.1k
Ruby is Unlike a Banana
tanoku
97
11k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
jQuery: Nuts, Bolts and Bling
dougneiner
61
7.5k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
131
33k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
It's Worth the Effort
3n
183
27k
Facilitating Awesome Meetings
lara
50
6.1k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
232
17k
Into the Great Unknown - MozCon
thekraken
32
1.5k
Transcript
Software Developer, Newlogic Arnelle Balane @arnellebalane Building PWAs using Workbox
Arnelle Balane Software Developer at Newlogic Google Developers Expert for
Web Technologies I write about Web stuff on my blog, arnellebalane.com @arnellebalane
None
Progressive Web Apps Websites built using Web technologies, and act
and feel like native apps
❖ Can be installed to the home screen ❖ Works
well even in intermittent or unreliable network conditions ❖ Works on any device ❖ Send notifications even when app is closed Progressive Web Apps
❖ Usually built using the application shell model, minimizing page
refresh ❖ Generally easier to deploy and maintain than native apps ❖ SEO, URLs, etc. Progressive Web Apps
Service Workers <script> if (navigator.serviceWorker) { window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js'); }); } </script>
Service Workers // sw.js addEventListener('install', event => { /**/ });
addEventListener('activate', event => { /**/ }); addEventListener('fetch', event => { /**/ });
❖ Service Workers ❖ Cache API ❖ Push Notifications ❖
Background Sync ❖ Background Fetch ❖ Web Application Manifest Moar APIs for our PWAs!
None
Workbox JavaScript libraries for adding offline support to Web applications
❖ from CDN ❖ Node module ❖ Webpack plugin Using
Workbox
Credits to Afrian Hanafi dribbble.com/shots/13125619-Makers-Workshop-Concept-II
Credits to Afrian Hanafi dribbble.com/shots/13125619-Makers-Workshop-Concept-II 2 pages 2 images 1
stylesheet 1 script
Import the library // sw.js importScripts('https://storage.googleapis.com/workbox-cdn/ releases/5.1.2/workbox-sw.js');
Precache site assets (deprecated) workbox.precaching.precache([ '/', '/classes', '/index.css', '/index.js', '/images/image-01.jpg',
'/images/image-02.jpg' ]);
Precache site assets, with versioning workbox.precaching.precache([ {url: '/', revision: 'v1'},
{url: '/classes', revision: 'v1'}, {url: '/index.css', revision: 'v1'}, {url: '/index.js', revision: 'v1'}, {url: '/images/image-01.jpg', revision: 'v1'}, {url: '/images/image-02.jpg', revision: 'v1'} ]);
Verify cache in Chrome Devtools
Y u no work?
Route precached assets workbox.precaching.precache([ /* ... */ ]); workbox.precaching.addRoute();
Precache and route site assets workbox.precaching.precacheAndRoute([ {url: '/', revision: 'v1'},
{url: '/classes', revision: 'v1'}, {url: '/index.css', revision: 'v1'}, {url: '/index.js', revision: 'v1'}, {url: '/images/image-01.jpg', revision: 'v1'}, {url: '/images/image-02.jpg', revision: 'v1'} ]);
Now it works offline!
Precaching Adding items to the cache in advance, even before
they are used
Gradually adding items to the cache as they are requested
Runtime caching
Cache a specific URL at runtime workbox.routing.registerRoute( '/images/image-01.jpg', new workbox.strategies.NetworkFirst()
);
Verify cache in Chrome Devtools
Cache a URL pattern at runtime workbox.routing.registerRoute( /\.jpg$/, new workbox.strategies.NetworkFirst()
);
Runtime caching using a “match callback” workbox.routing.registerRoute( (context) => context.url.origin
=== 'https://arnelle.me', new workbox.strategies.NetworkFirst() ); workbox.routing.registerRoute( (context) => context.request.destination === 'image', new workbox.strategies.NetworkFirst() );
Runtime caching https://developers.google.com/web/tools/workbox/reference-docs /latest/module-workbox-routing Precaching https://developers.google.com/web/tools/workbox/reference-docs /latest/module-workbox-precaching More Info
Workbox provides simple implementations for common service worker caching strategies
Caching strategies
Network-First Image from web.dev
Network-First workbox.routing.registerRoute( /\.jpg$/, new workbox.strategies.NetworkFirst() );
Network-First workbox.routing.registerRoute( /\.jpg$/, new workbox.strategies.NetworkFirst({ networkTimeoutSeconds: 3 }) );
Cache-First Image from web.dev
Cache-First workbox.routing.registerRoute( /\.css$/, new workbox.strategies.CacheFirst() );
Stale-While-Revalidate Image from web.dev
Stale-While-Revalidate workbox.routing.registerRoute( (context) => context.url.pathname.startsWith('/blog'), new workbox.strategies.StaleWhileRevalidate() );
❖ Nothing special other than they can use Workbox Plugins
Cache-Only Network-Only
The Offline Cookbook https://developers.google.com/web/fundamentals/instant-and -offline/offline-cookbook Workbox Strategies https://developers.google.com/web/tools/workbox/reference-docs /latest/module-workbox-strategies More
Info
Additional service worker behaviours without writing more boilerplate code Workbox
plugins
❖ ExpirationPlugin ❖ BroadcastUpdatePlugin ❖ BackgroundSyncPlugin ❖ CacheableResponsePlugin ❖ RangeRequestsPlugin
Workbox plugins
Using plugins workbox.routing.registerRoute( /\.jpg$/, new workbox.strategies.CacheFirst({ plugins: [ /* plugins
here */ ] }) );
ExpirationPlugin, expire after some time workbox.routing.registerRoute( /\.jpg$/, new workbox.strategies.CacheFirst({ cacheName:
'image-cache', plugins: [ new workbox.expiration.ExpirationPlugin({ maxAgeSeconds: 10 }) ] }) );
ExpirationPlugin, custom cache name workbox.routing.registerRoute( /\.jpg$/, new workbox.strategies.CacheFirst({ cacheName: 'image-cache',
plugins: [ new workbox.expiration.ExpirationPlugin({ maxAgeSeconds: 10 }) ] }) );
ExpirationPlugin, limit number of entries workbox.routing.registerRoute( /\.jpg$/, new workbox.strategies.CacheFirst({ cacheName:
'image-cache', plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1 }) ] }) );
ExpirationPlugin
ExpirationPlugin
BroadcastUpdatePlugin workbox.routing.registerRoute( /\/blog\/.+$/, new workbox.strategies.StaleWhileRevalidate({ plugins: [ new workbox.broadcastUpdate.BroadcastUpdatePlugin() ]
}) );
BroadcastUpdatePlugin, handle in main page // index.js navigator.serviceWorker.onmessage = event
=> { if (event.data.meta === 'workbox-broadcast-update') { displayUpdateAvailableUI(); } };
BroadcastUpdatePlugin, handle in main page // index.js navigator.serviceWorker.onmessage = event
=> { if (event.data.meta === 'workbox-broadcast-update') { displayUpdateAvailableUI(); } };
BroadcastUpdatePlugin
Custom Plugins https://developers.google.com/web/tools/workbox/guides /using-plugins#custom_plugins Using Plugins https://developers.google.com/web/tools/workbox/guides /using-plugins More Info
Use Workbox with popular JavaScript frameworks. Webpack plugin
Install dependencies npm install -D workbox-webpack-plugin
GenerateSW plugin const {GenerateSW} = require('workbox-webpack-plugin'); module.exports = { /*
... */ plugins: [ new GenerateSW({ swDest: 'sw.js' }) ] };
InjectManifest plugin const {InjectManifest} = require('workbox-webpack-plugin'); module.exports = { /*
... */ plugins: [ new InjectManifest({ swSrc: './www/sw.js' }) ] };
InjectManifest plugin // sw.js importScripts('https://storage.googleapis.com/...'); workbox.precaching.precacheAndRoute([ {url: '/', revision: 'v1'},
{url: '/classes', revision: 'v1'}, {url: '/index.css', revision: 'v1'}, {url: '/index.js', revision: 'v1'} ]);
InjectManifest plugin, add injection point // sw.js importScripts('https://storage.googleapis.com/...'); workbox.precaching.precacheAndRoute( self.__WB_MANIFEST
);
InjectManifest plugin, build result // dist/sw.js importScripts('https://storage.googleapis.com/...'); workbox.precaching.precacheAndRoute([ {'revision':'b91028...','url':'index.329fc4.js'}, {'revision':'20420e...','url':'index.html'}
]);
Update service worker imports // sw.js import {precacheAndRoute} from 'workbox-precaching';
precacheAndRoute(self.__WB_MANIFEST);
Update service worker imports import {registerRoute} from 'workbox-routing'; import {StaleWhileRevalidate}
from 'workbox-strategies'; import {BroadcastUpdatePlugin} from 'workbox-broadcast-update'; registerRoute( /\/blog\/.+$/, new StaleWhileRevalidate({ plugins: [new BroadcastUpdatePlugin()] }) );
Webpack plugin reference https://developers.google.com/web/tools/workbox/reference-doc s/latest/module-workbox-webpack-plugin Using bundlers with Workbox https://developers.google.com/web/tools/workbox/guides
/using-bundlers More Info
a.k.a I didn’t know where to put these content so
I’ll just put them at the end... Common recipes
Cache Google Fonts workbox.routing.registerRoute( (context) => context.url.origin === 'https://fonts.googleapis.com', new
workbox.strategies.StaleWhileRevalidate({ cacheName: 'google-fonts-stylesheets' }) );
Cache Google Fonts workbox.routing.registerRoute( (context) => context.url.origin === 'https://fonts.gstatic.com', new
workbox.strategies.CacheFirst({ cacheName: 'google-fonts-webfonts', plugins: [ new workbox.expiration.ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 365, // one year maxEntries: 30 }) ] }) );
Generic fallbacks
Generic fallbacks
Generic fallbacks, precache fallback image workbox.precaching.precache([ {url: '/images/fallback-image.png', revision: 'v1'},
]);
Generic fallbacks, route all images workbox.precaching.precache([ {url: '/images/fallback-image.png', revision: 'v1'},
]); workbox.routing.registerRoute( (context) => context.request.destination === 'image', new workbox.strategies.NetworkOnly() );
Generic fallbacks workbox.routing.setCatchHandler(async context => { if (context.request.destination === 'image')
{ return workbox.precaching.matchPrecache( '/images/fallback-image.png' ); } });
Generic fallbacks, respond with fallback image workbox.routing.setCatchHandler(async context => {
if (context.request.destination === 'image') { return workbox.precaching.matchPrecache( '/images/fallback-image.png' ); } });
Generic fallbacks
Generic fallbacks, for other pages
Workbox https://developers.google.com/web/tools/workbox Common Recipes https://developers.google.com/web/tools/workbox/guides /common-recipes More Info
❖ Workbox caching strategies ❖ Workbox plugins ❖ Using Workbox
with Webpack ❖ Common Workbox recipes Recap!
❖ Web Application Manifest ❖ Push Notifications ❖ Background Sync
❖ Web Capabilities What’s next?
Building PWAs using Workbox Arnelle Balane That’s all for today!
Thank you!