[DRAFT]
Service Workers: understand and use

Stepan Suvorov

-
CTO @
-
Google Developer Expert at Angular
-
with Angular since 2013
-
🇺🇦 Angular Kharkiv Meetup organizer
-
Angular Workshops on javascript.info
-




#standWithUkraine 🇺🇦
Agenda



?
use
Angular
Service Workers?
ServiceWorkers
vs
WebWorkers

ServiceWorkers
vs
Angular Interceptors
Angular Interceptors
@Injectable()
export class RetryInterceptor implements HttpInterceptor {
intercept(httpRequest: HttpRequest<any>,
next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(httpRequest).pipe(retry(2));
}
}
- Authentication
- Caching
- Profiling
- Error handling
- Mock backend
- Loaders & Notifications
- Headers management
- ....
Angular Interceptors
Interceptors vs ServiceWorkers

Angular
HttpClient
Interceptors
ServiceWorkers

Interceptors vs ServiceWorkers
HttpClient
All requests
- the page itself (index.html)
- assets (js, images, css)
- AJAX (XMLHttpRequest, fetch)
What are Service Workers?
- can't access the DOM directly
- control how network requests from your page
- async → extensive use of promises
- HTTPS required
- Reliability
- Performance
- Push Notifications
Use
navigator.serviceWorker.register("/sw.js");
navigator.serviceWorker.register"/sw.js",
{ scope: "/"});
Registering ServiceWorker
Why is my service worker failing to register?
This could be for the following reasons:
- You are not running your application through HTTPS.
- The path to your service worker file is not written correctly — it needs to be written relative to the origin, not your app's root directory. In our example, the worker is at
https://bncb2v.csb.app/sw.js
, and the app's root ishttps://bncb2v.csb.app/
. But the path needs to be written as/sw.js
. - It is also not allowed to point to a service worker of a different origin than that of your app.
sw.js
ServiceWorker Events
install
self.addEventListener("install", (event) => { //... });
self.addEventListener("install", (event) => {
event.waitUntil(addToCache(["/", "/index.html", "/some.js"...]));
});
const addToCache = async (resources) => {
const cache = await caches.open("v1");
await cache.addAll(resources);
};
activate
activate vs install

fetch
self.addEventListener("fetch", (event) => {
event.respondWith(caches.match(event.request));
});
message
navigator.serviceWorker.ready.then((registration) => {
registration.active.postMessage("Hi service worker");
});
addEventListener("message", (event) => {
// event is an ExtendableMessageEvent object
console.log(`The client sent me a message: ${event.data}`);
event.source.postMessage("Hi client");
});
navigator.serviceWorker.addEventListener('message', (event) => {
console.log(event.data);
});
sync
self.addEventListener('periodicsync', (event) => {
if (event.tag === 'fetch-news') {
event.waitUntil(fetchAndCacheLatestNews());
}
});
const registration = await navigator.serviceWorker.ready;
await registration.periodicSync.register('fetch-news', {
minInterval: 24 * 60 * 60 * 1000,
});
push
self.addEventListener("push", (event) => {
const message = event.data.json();
});
Security Time
Do you know how many SWs already registered in your browser?
Service Workers
in Angular
Install with AngularCli
ng add @angular/pwa
- @angular/service-worker package
- index.html
- build config →  sw bundle to dist
- SW in the app module
- ngsw-manifest.json
- offline mode (cache)
Cache our of box
What's being cached?
- index.html
- favicon.ico
- build artifacts (js and css bundles)
- assets

ngsw-config.json
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/manifest.webmanifest",
"/*.css",
"/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}
ngsw-config
- installMode
- updateMode
- prefetch
- lazy
Caching External Resources
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
...
{
"name": "externals",
"installMode": "lazy",
"updateMode": "lazy",
"resources": {
"urls": ["https://angular.io/assets/images/logos/angular/angular.svg"]
}
}
...
]
}
Caching HTTP Requests
{
...
"assetGroups": [ ... ],
"dataGroups": [
{
...
},
{
...
}
]
}
dataGroups
export interface DataGroup {
name: string;
urls: string[];
version?: number;
cacheConfig: {
maxSize: number;
maxAge: string;
timeout?: string;
strategy?: 'freshness' | 'performance';
};
cacheQueryOptions?: {
ignoreSearch?: boolean;
};
}
dataGroups
strategy: 'freshness' | 'performance';
-
performance(default) - cache first
-
freshness - network first
dataGroups
"dataGroups": [
{
"name": "random.org",
"urls": ["https://api.random.org/**"],
"cacheConfig": {
"maxSize": 20,
"maxAge": "7d",
"strategy": "freshness"
}
}
]
Updates
constructor(swUpdate: SwUpdate) {
swUpdate.available.subscribe(event => {
console.log('UPDATE!');
// user prompt?
// document.location.reload());
});
}
Thank you for your attention.
Questions?
Feedback time

Service Workers: understand and use
By Stepan Suvorov
Service Workers: understand and use
- 249