Espen Henriksen
Front-end team lead
Oslo Market Solutions
espen_dev
Designing for performance with Service Workers
What are service workers?
In a nutshell
- An event driven worker
- Served as a separate JavaScript-file
- Has special APIs
- Acts as a programmable caching proxy inside the browser
- Will control a page and all subsequent visits
- Runs as a separate thread which will keep running even when the browser tab is closed
Lifecycle
- Progresses through the lifecycle until it reaches "activated"
- Only one service worker can be "activated" for each website
- Once a newer worker is activated, the old one is made redundant and discarded
- Waits until existing worker closes unless told to skip waiting
Caching
- Intercepts all requests
- Can serve from cache or send the request to the network
- Can be programmed to one of many caching strategies
- Precaching
- Cache all static assets in the install step
- workbox-webpack-plugin
Precaching
- Preloads all static assets into the cache in the service worker's install stage
- JS, CSS, fonts, etc..
- Uses browser idle time
- Ensures all subsequent navigation is fast
Precaching
Precaching
All requests are fetched from cache.
The "Cache only" strategy
Network falling back to cache
- Always fetch from network
- Update cache when response arrives
- Use cache when network is down
- Quick fix for getting offline support
Network falling back to cache
Cache then network
- Like network-then-cache, but faster
- Show cached data first
- Update view with fresh data when response returns
- Best of both worlds, fast and supports offline
Cache then network
Still awake?
Design Patterns
PRPL
Push
- Push critical resources for the initial URL route.
- Fonts, CSS, JS chunks
- HTTP/2
- "But HTTP/2 server push is not cache aware!"
- index.html is only served once, during service worker install
Render
- Render initial route
- App shell architecture
- User sees "shell" to indicate that loading is in progress
- View loads when it is ready
Pre-cache
- Pre-cache remaining routes
- Service worker boots up
- Installs all static assets into cache
Lazy-load
- Lazy-load and create remaining routes on demand
- When user navigates, pull routes from cache
- Fast response to user actions
Push
Render
Pre-cache
Lazy-load
Developing service workers
Workbox
- A library for building service workers programatically
- workbox-webpack-plugin
- Automatically precaches webpack chunks
Workbox example
// Inside of webpack.config.js:
const WorkboxPlugin = require('workbox-webpack-plugin');
module.exports = {
// Other webpack config...
plugins: [
// Other plugins...
new WorkboxPlugin.GenerateSW()
]
};
Workbox example with runtime caching
// Inside of webpack.config.js:
const WorkboxPlugin = require('workbox-webpack-plugin');
module.exports = {
plugins: [
new WorkboxPlugin.GenerateSW({
// Define runtime caching rules.
runtimeCaching: [
{
// Match any request ends with .png, .jpg, .jpeg or .svg.
urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
// Apply a cache-first strategy.
handler: 'cacheFirst',
options: {
// Only cache 10 images.
expiration: {
maxEntries: 10,
},
},
},
],
})
]
};
Finally, register the worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
// Register returns a promise
}
Devtools demo
Pitfalls
Security limitations
The worker file
- Must not be cached
- An earlier version of the spec allowed it to be cached up to 24 hours
- Now all browsers should treat it as no-cache
- Immutable filename
- (No cache key in name)
Deploys
- Changes the loading paradigm we know
- New service worker discovered after load
- Client will run stale code after deploy
- We can auto-refresh the browser on upgrade
- Better solution: Inform user of a new version and nag them to refresh
Rollback
- You can't simply delete service worker if things break
- Only way to replace running workers is by deploying a "no-op" worker
Thanks for listening!
Questions?
Designing for performance with Service Workers
By Eline H
Designing for performance with Service Workers
- 32