26. @AaronGustafson
Registering a Service Worker
if ( "serviceWorker" in navigator ) {
navigator.serviceWorker.register( "/serviceworker.min.js" );
}
Path is important!
39. @AaronGustafson
Make good choices
2. Use responsive images
39
<img src="medium.jpg"
srcset="small.jpg 256w,
medium.jpg 512w,
large.jpg 1024w"
sizes="(max-width: 30em) 30em, 100vw"
alt="It’s responsive!">
aka.ms/cloudinary-images
40. @AaronGustafson
Make good choices
3. Lazy load images
40
<img src="medium.jpg"
srcset="small.jpg 256w,
medium.jpg 512w,
large.jpg 1024w"
sizes="(max-width: 30em) 30em, 100vw"
loading="lazy"
alt="It’s responsive and lazy loads!">
aka.ms/img-lazy-loading
41. @AaronGustafson
Make good choices
4. Provide alternate image formats
41
<picture>
<source type="image/webp" srcset="my.webp">
<img src="my.jpg" alt="Alt text goes here">
</picture>
42. @AaronGustafson
Make good choices
4. Provide alternate image formats (via Cloudinary URLs)
42
https://res.cloudinary.com/demo/image/upload/w_300,f_auto/my.jpg
aka.ms/cloudinary-webp
43. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
43
self.addEventListener( "install", function( event ){
event.waitUntil(
caches.open( "static" ).then( cache => {
return cache.addAll( ["/i/fallbacks/offline.svg"] );
})
);
});
function respondWithOfflineImage() {
return caches.match( "/i/fallbacks/offline.svg" );
}
aka.ms/ag-sw
44. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
44
self.addEventListener( "install", function( event ){
event.waitUntil(
caches.open( "static" ).then( cache => {
return cache.addAll( ["/i/fallbacks/offline.svg"] );
})
);
});
function respondWithOfflineImage() {
return caches.match( "/i/fallbacks/offline.svg" );
}
aka.ms/ag-sw
45. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
45
self.addEventListener( "install", function( event ){
event.waitUntil(
caches.open( "static" ).then( cache => {
return cache.addAll( ["/i/fallbacks/offline.svg"] );
})
);
});
function respondWithOfflineImage() {
return caches.match( "/i/fallbacks/offline.svg" );
}
aka.ms/ag-sw
46. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
46
self.addEventListener( "install", function( event ){
event.waitUntil(
caches.open( "static" ).then( cache => {
return cache.addAll( ["/i/fallbacks/offline.svg"] );
})
);
});
function respondWithOfflineImage() {
return caches.match( "/i/fallbacks/offline.svg" );
}
aka.ms/ag-sw
47. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
47
self.addEventListener( "install", function( event ){
event.waitUntil(
caches.open( "static" ).then( cache => {
return cache.addAll( ["/i/fallbacks/offline.svg"] );
})
);
});
function respondWithOfflineImage() {
return caches.match( "/i/fallbacks/offline.svg" );
}
aka.ms/ag-sw
48. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
48
self.addEventListener( "install", function( event ){
event.waitUntil(
caches.open( "static" ).then( cache => {
return cache.addAll( ["/i/fallbacks/offline.svg"] );
})
);
});
function respondWithOfflineImage() {
return caches.match( "/i/fallbacks/offline.svg" );
}
aka.ms/ag-sw
49. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
49
self.addEventListener( "fetch", event => {
const request = event.request,
url = request.url;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
return fetch( request, fetch_config.images )
.then( response => {
return response;
})
.catch(
respondWithOfflineImage
);
);
}
});
aka.ms/ag-sw
50. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
50
self.addEventListener( "fetch", event => {
const request = event.request,
url = request.url;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
return fetch( request, fetch_config.images )
.then( response => {
return response;
})
.catch(
respondWithOfflineImage
);
);
}
});
aka.ms/ag-sw
51. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
51
self.addEventListener( "fetch", event => {
const request = event.request,
url = request.url;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
return fetch( request, fetch_config.images )
.then( response => {
return response;
})
.catch(
respondWithOfflineImage
);
);
}
});
aka.ms/ag-sw
52. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
52
self.addEventListener( "fetch", event => {
const request = event.request,
url = request.url;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
return fetch( request, fetch_config.images )
.then( response => {
return response;
})
.catch(
respondWithOfflineImage
);
);
}
});
aka.ms/ag-sw
53. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
53
self.addEventListener( "fetch", event => {
const request = event.request,
url = request.url;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
return fetch( request, fetch_config.images )
.then( response => {
return response;
})
.catch(
respondWithOfflineImage
);
);
}
});
aka.ms/ag-sw
54. @AaronGustafson
Make good choices
5. Provide fallback images via Service Worker
54
self.addEventListener( "fetch", event => {
const request = event.request,
url = request.url;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
return fetch( request, fetch_config.images )
.then( response => {
return response;
})
.catch(
respondWithOfflineImage
);
);
}
});
aka.ms/ag-sw
55. @AaronGustafson
Make good choices
6. Pay attention to the Save Data header
55
let save_data = false;
if ( 'connection' in navigator ) {
save_data = navigator.connection.saveData;
}
aka.ms/ag-sw
56. @AaronGustafson
Make good choices
6. Pay attention to the Save Data header
56
self.addEventListener( "fetch", event => {
const request = event.request,
url = request.url;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
if ( save_data ) {
return respondWithFallbackImage( url );
}
// …
);
}
aka.ms/ag-sw
57. @AaronGustafson
Make good choices
6. Pay attention to the Save Data header
57
const fallback_avatar = "/i/fallbacks/avatar.svg",
fallback_image = "/i/fallbacks/image.svg",
avatars = /webmention.io/;
// …
function respondWithFallbackImage( url ) {
const image = avatars.test( url ) ? fallback_avatar
: fallback_image;
return caches.match( image );
}
aka.ms/ag-sw
58. @AaronGustafson
Make good choices
6. Pay attention to the Save Data header
58
const fallback_avatar = "/i/fallbacks/avatar.svg",
fallback_image = "/i/fallbacks/image.svg",
avatars = /webmention.io/;
// …
function respondWithFallbackImage( url ) {
const image = avatars.test( url ) ? fallback_avatar
: fallback_image;
return caches.match( image );
}
aka.ms/ag-sw
59. @AaronGustafson
Make good choices
6. Pay attention to the Save Data header
59
const fallback_avatar = "/i/fallbacks/avatar.svg",
fallback_image = "/i/fallbacks/image.svg",
avatars = /webmention.io/;
// …
function respondWithFallbackImage( url ) {
const image = avatars.test( url ) ? fallback_avatar
: fallback_image;
return caches.match( image );
}
aka.ms/ag-sw
60. @AaronGustafson
Make good choices
7. Prioritize certain images
60
const high_priority = [
/aaron-gustafson.com/,
/adaptivewebdesign.info/
];
function isHighPriority( url ) {
let i = high_priority.length;
while ( i-- ) {
if ( high_priority[i].test( url ) ) {
return true;
}
}
return false;
}
aka.ms/ag-sw
61. @AaronGustafson
Make good choices
7. Prioritize certain images
61
const high_priority = [
/aaron-gustafson.com/,
/adaptivewebdesign.info/
];
function isHighPriority( url ) {
let i = high_priority.length;
while ( i-- ) {
if ( high_priority[i].test( url ) ) {
return true;
}
}
return false;
}
aka.ms/ag-sw
62. @AaronGustafson
Make good choices
7. Prioritize certain images
62
const high_priority = [
/aaron-gustafson.com/,
/adaptivewebdesign.info/
];
function isHighPriority( url ) {
let i = high_priority.length;
while ( i-- ) {
if ( high_priority[i].test( url ) ) {
return true;
}
}
return false;
}
aka.ms/ag-sw
64. @AaronGustafson
Make good choices
8. Clean up after yourself
64
function trimCache( cache_name, limit ) {
caches.open( cache_name ).then( cache => {
cache.keys().then( items => {
if ( items.length > limit ) {
cache.delete( items[0] ).then(
trimCache( cache_name, limit )
);
}
});
});
}
aka.ms/ag-sw
65. @AaronGustafson
Make good choices
8. Clean up after yourself
65
function trimCache( cache_name, limit ) {
caches.open( cache_name ).then( cache => {
cache.keys().then( items => {
if ( items.length > limit ) {
cache.delete( items[0] ).then(
trimCache( cache_name, limit )
);
}
});
});
}
aka.ms/ag-sw
66. @AaronGustafson
Make good choices
8. Clean up after yourself
66
function trimCache( cache_name, limit ) {
caches.open( cache_name ).then( cache => {
cache.keys().then( items => {
if ( items.length > limit ) {
cache.delete( items[0] ).then(
trimCache( cache_name, limit )
);
}
});
});
}
aka.ms/ag-sw
67. @AaronGustafson
Make good choices
8. Clean up after yourself
67
if ( "serviceWorker" in navigator ) {
navigator.serviceWorker.register( "/serviceworker.min.js" );
if ( navigator.serviceWorker.controller ) {
window.addEventListener( "load", function(){
navigator.serviceWorker.controller.postMessage( "clean up" );
});
}
}
aka.ms/ag-sw
68. @AaronGustafson
Make good choices
8. Clean up after yourself
68
addEventListener("message", messageEvent => {
if (messageEvent.data == "clean up") {
for ( let key in sw_caches ) {
if ( sw_caches[key].limit != undefined ) {
trimCache( sw_caches[key].name, sw_caches[key].limit );
}
}
}
});
aka.ms/ag-sw
69. @AaronGustafson
Make good choices
8. Clean up after yourself
69
addEventListener("message", messageEvent => {
if (messageEvent.data == "clean up") {
for ( let key in sw_caches ) {
if ( sw_caches[key].limit != undefined ) {
trimCache( sw_caches[key].name, sw_caches[key].limit );
}
}
}
});
aka.ms/ag-sw
70. @AaronGustafson
Make good choices
1. No animated GIFs (especially as backgrounds)
2. Use responsive images
3. Lazy load images
4. Provide alternate image formats
5. Provide fallback images via Service Worker
6. Pay attention to the Save Data header
7. Prioritize certain images
8. Clean up after yourself
70