SlideShare ist ein Scribd-Unternehmen logo
1 von 72
Downloaden Sie, um offline zu lesen
Media in the
Age of PWAs
Aaron Gustafson
@AaronGustafson
slideshare.net/AaronGustafson
What exactly is a
Progressive Web App?
© Brad Frost
What exactly is a
Progressive Web App?
© Brad Frost
“Progressive Web App”
is a marketing term
Progressive Web App
© Brad Frost
Progressive Web App
Progressive Web App
Game
Gallery
Book
Newspaper
Art Project
Tool
Progressive Web App
Game
Gallery
Book
Newspaper
Art Project
Tool
Progressive Web Site
Who’s behind PWAs?
@AaronGustafson
A Minimum Viable PWA
HTTPS
@AaronGustafson
A Minimum Viable PWA
HTTPS Web App
Manifest
@AaronGustafson
Web App Manifest
{
"lang": "en",
"short_name": "Wash Post",
"name": "The Washington Post",
"icons": [ { "src": "img/launcher-icon-2x.png",
"sizes": "96x96",
"type": "image/png" } ],
"start_url": "/pwa/",
"display": "standalone",
"orientation": "portrait",
"background_color": "black"
}
@AaronGustafson
A Minimum Viable PWA
HTTPS Web App
Manifest
Service
Worker
Should you
believe the hype?
Maybe?
Carnival:
24% opt-in rate and
42% open rate for
push notifications
Katarzyna Ostrowska
aka.ms/carnival-pwa
Starbucks:
2x increase in daily
active users
aka.ms/google-io-2018
Tinder:
Core experience
with 90% less code
aka.ms/tinder-pwa-2017
Trivago:
97% increase in
click-outs to
hotel offers
aka.ms/trivago-pwa-2017
West Elm:
15% increase in
time on site
9% increase in
revenue per visit
aka.ms/west-elm-pwa-2017
@AaronGustafson
A Minimum Viable PWA
HTTPS Web App
Manifest
Service
Worker
@AaronGustafson
Let’s talk about Service Worker
@AaronGustafson
Registering a Service Worker
if ( "serviceWorker" in navigator ) {
navigator.serviceWorker.register( "/serviceworker.min.js" );
}
@AaronGustafson
Registering a Service Worker
if ( "serviceWorker" in navigator ) {
navigator.serviceWorker.register( "/serviceworker.min.js" );
}
@AaronGustafson
Registering a Service Worker
if ( "serviceWorker" in navigator ) {
navigator.serviceWorker.register( "/serviceworker.min.js" );
}
@AaronGustafson
Registering a Service Worker
if ( "serviceWorker" in navigator ) {
navigator.serviceWorker.register( "/serviceworker.min.js" );
}
Path is important!
@AaronGustafson
The Service Worker Lifecycle
Browser
Install Activation Ready
aka.ms/pwa-lifecycle
@AaronGustafson
How connections are made
Browser
Internet
@AaronGustafson
Along comes Service Worker
Browser
Internet
@AaronGustafson
Along comes Service Worker
Browser
Internet
Cache
@AaronGustafson
Along comes Service Worker
Browser
Internet
Cache
!
@AaronGustafson
Know your (storage) limits
Temporary Persistent
Browser purges User purges
@AaronGustafson
Know your (storage) limits
Volume Size Domain Limit Overall Limit
≤ 8 GB
20%
of
overall
50 MB
8–32 GB 500 MB
32–128 GB 4% of volume
> 128 GB 4% or 20 GB
Except on iOS.
Safari gives you 50 MB.
Raising limits?
Unlimited storage?
Storage is a privilege,
don’t abuse it.
How?
@AaronGustafson
Make good choices
1. No animated GIFs (especially as backgrounds)
38
@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
@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
@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>
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@AaronGustafson
Make good choices
8. Clean up after yourself
63
const version = "v2:",
sw_caches = {
static: {
name: `${version}static`
},
images: {
name: `${version}images`,
limit: 75
},
pages: {
name: `${version}pages`,
limit: 5
},
posts: {
name: `${version}posts`,
limit: 10
},
other: {
name: `${version}other`,
limit: 50
}
};
aka.ms/ag-sw
@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
@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
@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
@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
@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
@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
@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
© Marvel
Thank you!
@AaronGustafson
aaron-gustafson.com
slideshare.net/AaronGustafson

Weitere ähnliche Inhalte

Was ist angesagt?

Intro to @viewport & other new Responsive Web Design CSS features
Intro to @viewport & other new Responsive Web Design CSS featuresIntro to @viewport & other new Responsive Web Design CSS features
Intro to @viewport & other new Responsive Web Design CSS featuresAndreas Bovens
 
Илья Пухальский (EPAM Systems)
Илья Пухальский (EPAM Systems)Илья Пухальский (EPAM Systems)
Илья Пухальский (EPAM Systems)Ontico
 
AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)
AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)
AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)Future Insights
 
Html5 on Mobile(For Developer)
Html5 on Mobile(For Developer)Html5 on Mobile(For Developer)
Html5 on Mobile(For Developer)Adam Lu
 
Richard Cole of Amazon Gives Lightning Tallk at BigDataCamp
Richard Cole of Amazon Gives Lightning Tallk at BigDataCampRichard Cole of Amazon Gives Lightning Tallk at BigDataCamp
Richard Cole of Amazon Gives Lightning Tallk at BigDataCampBigDataCamp
 

Was ist angesagt? (8)

Responsive Enhancement
Responsive EnhancementResponsive Enhancement
Responsive Enhancement
 
Site optimization
Site optimizationSite optimization
Site optimization
 
Bilder usw...
Bilder usw...Bilder usw...
Bilder usw...
 
Intro to @viewport & other new Responsive Web Design CSS features
Intro to @viewport & other new Responsive Web Design CSS featuresIntro to @viewport & other new Responsive Web Design CSS features
Intro to @viewport & other new Responsive Web Design CSS features
 
Илья Пухальский (EPAM Systems)
Илья Пухальский (EPAM Systems)Илья Пухальский (EPAM Systems)
Илья Пухальский (EPAM Systems)
 
AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)
AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)
AngularJS: The Bridge Between Today and Tomorrow's Web (Todd Motto)
 
Html5 on Mobile(For Developer)
Html5 on Mobile(For Developer)Html5 on Mobile(For Developer)
Html5 on Mobile(For Developer)
 
Richard Cole of Amazon Gives Lightning Tallk at BigDataCamp
Richard Cole of Amazon Gives Lightning Tallk at BigDataCampRichard Cole of Amazon Gives Lightning Tallk at BigDataCamp
Richard Cole of Amazon Gives Lightning Tallk at BigDataCamp
 

Ähnlich wie Imagecon 2019 - Aaron Gustafson

The things browsers can do! SAE Alumni Convention 2014
The things browsers can do! SAE Alumni Convention 2014The things browsers can do! SAE Alumni Convention 2014
The things browsers can do! SAE Alumni Convention 2014Christian Heilmann
 
HTML5 after the hype - JFokus2015
HTML5 after the hype - JFokus2015HTML5 after the hype - JFokus2015
HTML5 after the hype - JFokus2015Christian Heilmann
 
Practical Responsive Images - from Second Wednesday
Practical Responsive Images - from Second WednesdayPractical Responsive Images - from Second Wednesday
Practical Responsive Images - from Second WednesdayBen Seymour
 
AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI colleenfry
 
Responsive images are here. Now what?
Responsive images are here. Now what?Responsive images are here. Now what?
Responsive images are here. Now what?Jason Grigsby
 
Web accessibility
Web accessibilityWeb accessibility
Web accessibilityEb Styles
 
Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4DEVCON
 
AtlasCamp 2015: Connect everywhere - Cloud and Server
AtlasCamp 2015: Connect everywhere - Cloud and ServerAtlasCamp 2015: Connect everywhere - Cloud and Server
AtlasCamp 2015: Connect everywhere - Cloud and ServerAtlassian
 
Responsive Responsive Design
Responsive Responsive DesignResponsive Responsive Design
Responsive Responsive DesignTim Kadlec
 
[CSSDevConf] Adaptive Images in Responsive Web Design 2014
[CSSDevConf] Adaptive Images in Responsive Web Design 2014[CSSDevConf] Adaptive Images in Responsive Web Design 2014
[CSSDevConf] Adaptive Images in Responsive Web Design 2014Christopher Schmitt
 
The Future of CSS with Web components
The Future of CSS with Web componentsThe Future of CSS with Web components
The Future of CSS with Web componentsdevObjective
 
The Future of CSS with Web Components
The Future of CSS with Web ComponentsThe Future of CSS with Web Components
The Future of CSS with Web ComponentsColdFusionConference
 
[rwdsummit] Adaptive Images in Responsive Web Design
[rwdsummit] Adaptive Images in Responsive Web Design[rwdsummit] Adaptive Images in Responsive Web Design
[rwdsummit] Adaptive Images in Responsive Web DesignChristopher Schmitt
 
Building Progressive Web Apps for Android and iOS
Building Progressive Web Apps for Android and iOSBuilding Progressive Web Apps for Android and iOS
Building Progressive Web Apps for Android and iOSFITC
 
Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...
Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...
Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...IT Event
 
[cssdevconf] Adaptive Images in Responsive Web Design
[cssdevconf] Adaptive Images in Responsive Web Design[cssdevconf] Adaptive Images in Responsive Web Design
[cssdevconf] Adaptive Images in Responsive Web DesignChristopher Schmitt
 
[psuweb] Adaptive Images in Responsive Web Design
[psuweb] Adaptive Images in Responsive Web Design[psuweb] Adaptive Images in Responsive Web Design
[psuweb] Adaptive Images in Responsive Web DesignChristopher Schmitt
 
[cssdevconf] Adaptive Images in RWD
[cssdevconf] Adaptive Images in RWD[cssdevconf] Adaptive Images in RWD
[cssdevconf] Adaptive Images in RWDChristopher Schmitt
 

Ähnlich wie Imagecon 2019 - Aaron Gustafson (20)

The things browsers can do! SAE Alumni Convention 2014
The things browsers can do! SAE Alumni Convention 2014The things browsers can do! SAE Alumni Convention 2014
The things browsers can do! SAE Alumni Convention 2014
 
HTML5 after the hype - JFokus2015
HTML5 after the hype - JFokus2015HTML5 after the hype - JFokus2015
HTML5 after the hype - JFokus2015
 
Practical Responsive Images - from Second Wednesday
Practical Responsive Images - from Second WednesdayPractical Responsive Images - from Second Wednesday
Practical Responsive Images - from Second Wednesday
 
AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI AtlasCamp 2013: Modernizing your Plugin UI
AtlasCamp 2013: Modernizing your Plugin UI
 
Progressive What Apps?
Progressive What Apps?Progressive What Apps?
Progressive What Apps?
 
Responsive images are here. Now what?
Responsive images are here. Now what?Responsive images are here. Now what?
Responsive images are here. Now what?
 
Web accessibility
Web accessibilityWeb accessibility
Web accessibility
 
Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4
 
AtlasCamp 2015: Connect everywhere - Cloud and Server
AtlasCamp 2015: Connect everywhere - Cloud and ServerAtlasCamp 2015: Connect everywhere - Cloud and Server
AtlasCamp 2015: Connect everywhere - Cloud and Server
 
Responsive Responsive Design
Responsive Responsive DesignResponsive Responsive Design
Responsive Responsive Design
 
[CSSDevConf] Adaptive Images in Responsive Web Design 2014
[CSSDevConf] Adaptive Images in Responsive Web Design 2014[CSSDevConf] Adaptive Images in Responsive Web Design 2014
[CSSDevConf] Adaptive Images in Responsive Web Design 2014
 
The Future of CSS with Web components
The Future of CSS with Web componentsThe Future of CSS with Web components
The Future of CSS with Web components
 
The Future of CSS with Web Components
The Future of CSS with Web ComponentsThe Future of CSS with Web Components
The Future of CSS with Web Components
 
[rwdsummit] Adaptive Images in Responsive Web Design
[rwdsummit] Adaptive Images in Responsive Web Design[rwdsummit] Adaptive Images in Responsive Web Design
[rwdsummit] Adaptive Images in Responsive Web Design
 
Building Progressive Web Apps for Android and iOS
Building Progressive Web Apps for Android and iOSBuilding Progressive Web Apps for Android and iOS
Building Progressive Web Apps for Android and iOS
 
Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...
Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...
Oleh Zasadnyy "Progressive Web Apps: line between web and native apps become ...
 
Backbone js
Backbone jsBackbone js
Backbone js
 
[cssdevconf] Adaptive Images in Responsive Web Design
[cssdevconf] Adaptive Images in Responsive Web Design[cssdevconf] Adaptive Images in Responsive Web Design
[cssdevconf] Adaptive Images in Responsive Web Design
 
[psuweb] Adaptive Images in Responsive Web Design
[psuweb] Adaptive Images in Responsive Web Design[psuweb] Adaptive Images in Responsive Web Design
[psuweb] Adaptive Images in Responsive Web Design
 
[cssdevconf] Adaptive Images in RWD
[cssdevconf] Adaptive Images in RWD[cssdevconf] Adaptive Images in RWD
[cssdevconf] Adaptive Images in RWD
 

Mehr von Cloudinary

Imagecon 2019 - Jon Sneyers
Imagecon 2019 - Jon Sneyers Imagecon 2019 - Jon Sneyers
Imagecon 2019 - Jon Sneyers Cloudinary
 
Imagecon 2019 - Jen Looper
Imagecon 2019 - Jen LooperImagecon 2019 - Jen Looper
Imagecon 2019 - Jen LooperCloudinary
 
Imagecon 2019 - Amy Balliett
Imagecon 2019 - Amy BalliettImagecon 2019 - Amy Balliett
Imagecon 2019 - Amy BalliettCloudinary
 
ImageCon CTO keynote
ImageCon CTO keynoteImageCon CTO keynote
ImageCon CTO keynoteCloudinary
 
ImageCon keynote product
ImageCon keynote productImageCon keynote product
ImageCon keynote productCloudinary
 
Drawing a Circle Three Ways: Generating Graphics for the Web
Drawing a Circle Three Ways: Generating Graphics for the WebDrawing a Circle Three Ways: Generating Graphics for the Web
Drawing a Circle Three Ways: Generating Graphics for the WebCloudinary
 
Images For Everyone
Images For EveryoneImages For Everyone
Images For EveryoneCloudinary
 
Beyond Resizing: The Image Performance Checklist
Beyond Resizing: The Image Performance ChecklistBeyond Resizing: The Image Performance Checklist
Beyond Resizing: The Image Performance ChecklistCloudinary
 
Moving Metrics with Better Mobile Images
Moving Metrics with Better Mobile ImagesMoving Metrics with Better Mobile Images
Moving Metrics with Better Mobile ImagesCloudinary
 
Images in the Era of the Algorithm
Images in the Era of the AlgorithmImages in the Era of the Algorithm
Images in the Era of the AlgorithmCloudinary
 
Media Processing Workflows using AWS Step Functions and Machine Learning on A...
Media Processing Workflows using AWS Step Functions and Machine Learning on A...Media Processing Workflows using AWS Step Functions and Machine Learning on A...
Media Processing Workflows using AWS Step Functions and Machine Learning on A...Cloudinary
 
The Physics of Fast Image Compression
The Physics of Fast Image CompressionThe Physics of Fast Image Compression
The Physics of Fast Image CompressionCloudinary
 

Mehr von Cloudinary (13)

Imagecon 2019 - Jon Sneyers
Imagecon 2019 - Jon Sneyers Imagecon 2019 - Jon Sneyers
Imagecon 2019 - Jon Sneyers
 
Imagecon 2019 - Jen Looper
Imagecon 2019 - Jen LooperImagecon 2019 - Jen Looper
Imagecon 2019 - Jen Looper
 
Imagecon 2019 - Amy Balliett
Imagecon 2019 - Amy BalliettImagecon 2019 - Amy Balliett
Imagecon 2019 - Amy Balliett
 
Imagecon Itai
Imagecon ItaiImagecon Itai
Imagecon Itai
 
ImageCon CTO keynote
ImageCon CTO keynoteImageCon CTO keynote
ImageCon CTO keynote
 
ImageCon keynote product
ImageCon keynote productImageCon keynote product
ImageCon keynote product
 
Drawing a Circle Three Ways: Generating Graphics for the Web
Drawing a Circle Three Ways: Generating Graphics for the WebDrawing a Circle Three Ways: Generating Graphics for the Web
Drawing a Circle Three Ways: Generating Graphics for the Web
 
Images For Everyone
Images For EveryoneImages For Everyone
Images For Everyone
 
Beyond Resizing: The Image Performance Checklist
Beyond Resizing: The Image Performance ChecklistBeyond Resizing: The Image Performance Checklist
Beyond Resizing: The Image Performance Checklist
 
Moving Metrics with Better Mobile Images
Moving Metrics with Better Mobile ImagesMoving Metrics with Better Mobile Images
Moving Metrics with Better Mobile Images
 
Images in the Era of the Algorithm
Images in the Era of the AlgorithmImages in the Era of the Algorithm
Images in the Era of the Algorithm
 
Media Processing Workflows using AWS Step Functions and Machine Learning on A...
Media Processing Workflows using AWS Step Functions and Machine Learning on A...Media Processing Workflows using AWS Step Functions and Machine Learning on A...
Media Processing Workflows using AWS Step Functions and Machine Learning on A...
 
The Physics of Fast Image Compression
The Physics of Fast Image CompressionThe Physics of Fast Image Compression
The Physics of Fast Image Compression
 

Kürzlich hochgeladen

Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...Dipal Arora
 
Keppel Ltd. 1Q 2024 Business Update Presentation Slides
Keppel Ltd. 1Q 2024 Business Update  Presentation SlidesKeppel Ltd. 1Q 2024 Business Update  Presentation Slides
Keppel Ltd. 1Q 2024 Business Update Presentation SlidesKeppelCorporation
 
Insurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usageInsurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usageMatteo Carbone
 
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service JamshedpurVIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service JamshedpurSuhani Kapoor
 
M.C Lodges -- Guest House in Jhang.
M.C Lodges --  Guest House in Jhang.M.C Lodges --  Guest House in Jhang.
M.C Lodges -- Guest House in Jhang.Aaiza Hassan
 
Regression analysis: Simple Linear Regression Multiple Linear Regression
Regression analysis:  Simple Linear Regression Multiple Linear RegressionRegression analysis:  Simple Linear Regression Multiple Linear Regression
Regression analysis: Simple Linear Regression Multiple Linear RegressionRavindra Nath Shukla
 
0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdf0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdfRenandantas16
 
Catalogue ONG NUOC PPR DE NHAT .pdf
Catalogue ONG NUOC PPR DE NHAT      .pdfCatalogue ONG NUOC PPR DE NHAT      .pdf
Catalogue ONG NUOC PPR DE NHAT .pdfOrient Homes
 
Pharma Works Profile of Karan Communications
Pharma Works Profile of Karan CommunicationsPharma Works Profile of Karan Communications
Pharma Works Profile of Karan Communicationskarancommunications
 
Best VIP Call Girls Noida Sector 40 Call Me: 8448380779
Best VIP Call Girls Noida Sector 40 Call Me: 8448380779Best VIP Call Girls Noida Sector 40 Call Me: 8448380779
Best VIP Call Girls Noida Sector 40 Call Me: 8448380779Delhi Call girls
 
Socio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptxSocio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptxtrishalcan8
 
Vip Dewas Call Girls #9907093804 Contact Number Escorts Service Dewas
Vip Dewas Call Girls #9907093804 Contact Number Escorts Service DewasVip Dewas Call Girls #9907093804 Contact Number Escorts Service Dewas
Vip Dewas Call Girls #9907093804 Contact Number Escorts Service Dewasmakika9823
 
Call Girls in Gomti Nagar - 7388211116 - With room Service
Call Girls in Gomti Nagar - 7388211116  - With room ServiceCall Girls in Gomti Nagar - 7388211116  - With room Service
Call Girls in Gomti Nagar - 7388211116 - With room Servicediscovermytutordmt
 
VIP Kolkata Call Girl Howrah 👉 8250192130 Available With Room
VIP Kolkata Call Girl Howrah 👉 8250192130  Available With RoomVIP Kolkata Call Girl Howrah 👉 8250192130  Available With Room
VIP Kolkata Call Girl Howrah 👉 8250192130 Available With Roomdivyansh0kumar0
 
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...lizamodels9
 
Cash Payment 9602870969 Escort Service in Udaipur Call Girls
Cash Payment 9602870969 Escort Service in Udaipur Call GirlsCash Payment 9602870969 Escort Service in Udaipur Call Girls
Cash Payment 9602870969 Escort Service in Udaipur Call GirlsApsara Of India
 
Sales & Marketing Alignment: How to Synergize for Success
Sales & Marketing Alignment: How to Synergize for SuccessSales & Marketing Alignment: How to Synergize for Success
Sales & Marketing Alignment: How to Synergize for SuccessAggregage
 
Ensure the security of your HCL environment by applying the Zero Trust princi...
Ensure the security of your HCL environment by applying the Zero Trust princi...Ensure the security of your HCL environment by applying the Zero Trust princi...
Ensure the security of your HCL environment by applying the Zero Trust princi...Roland Driesen
 

Kürzlich hochgeladen (20)

Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
Call Girls Navi Mumbai Just Call 9907093804 Top Class Call Girl Service Avail...
 
Keppel Ltd. 1Q 2024 Business Update Presentation Slides
Keppel Ltd. 1Q 2024 Business Update  Presentation SlidesKeppel Ltd. 1Q 2024 Business Update  Presentation Slides
Keppel Ltd. 1Q 2024 Business Update Presentation Slides
 
Insurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usageInsurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usage
 
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service JamshedpurVIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
VIP Call Girl Jamshedpur Aashi 8250192130 Independent Escort Service Jamshedpur
 
M.C Lodges -- Guest House in Jhang.
M.C Lodges --  Guest House in Jhang.M.C Lodges --  Guest House in Jhang.
M.C Lodges -- Guest House in Jhang.
 
Nepali Escort Girl Kakori \ 9548273370 Indian Call Girls Service Lucknow ₹,9517
Nepali Escort Girl Kakori \ 9548273370 Indian Call Girls Service Lucknow ₹,9517Nepali Escort Girl Kakori \ 9548273370 Indian Call Girls Service Lucknow ₹,9517
Nepali Escort Girl Kakori \ 9548273370 Indian Call Girls Service Lucknow ₹,9517
 
Regression analysis: Simple Linear Regression Multiple Linear Regression
Regression analysis:  Simple Linear Regression Multiple Linear RegressionRegression analysis:  Simple Linear Regression Multiple Linear Regression
Regression analysis: Simple Linear Regression Multiple Linear Regression
 
0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdf0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdf
 
Catalogue ONG NUOC PPR DE NHAT .pdf
Catalogue ONG NUOC PPR DE NHAT      .pdfCatalogue ONG NUOC PPR DE NHAT      .pdf
Catalogue ONG NUOC PPR DE NHAT .pdf
 
Pharma Works Profile of Karan Communications
Pharma Works Profile of Karan CommunicationsPharma Works Profile of Karan Communications
Pharma Works Profile of Karan Communications
 
Forklift Operations: Safety through Cartoons
Forklift Operations: Safety through CartoonsForklift Operations: Safety through Cartoons
Forklift Operations: Safety through Cartoons
 
Best VIP Call Girls Noida Sector 40 Call Me: 8448380779
Best VIP Call Girls Noida Sector 40 Call Me: 8448380779Best VIP Call Girls Noida Sector 40 Call Me: 8448380779
Best VIP Call Girls Noida Sector 40 Call Me: 8448380779
 
Socio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptxSocio-economic-Impact-of-business-consumers-suppliers-and.pptx
Socio-economic-Impact-of-business-consumers-suppliers-and.pptx
 
Vip Dewas Call Girls #9907093804 Contact Number Escorts Service Dewas
Vip Dewas Call Girls #9907093804 Contact Number Escorts Service DewasVip Dewas Call Girls #9907093804 Contact Number Escorts Service Dewas
Vip Dewas Call Girls #9907093804 Contact Number Escorts Service Dewas
 
Call Girls in Gomti Nagar - 7388211116 - With room Service
Call Girls in Gomti Nagar - 7388211116  - With room ServiceCall Girls in Gomti Nagar - 7388211116  - With room Service
Call Girls in Gomti Nagar - 7388211116 - With room Service
 
VIP Kolkata Call Girl Howrah 👉 8250192130 Available With Room
VIP Kolkata Call Girl Howrah 👉 8250192130  Available With RoomVIP Kolkata Call Girl Howrah 👉 8250192130  Available With Room
VIP Kolkata Call Girl Howrah 👉 8250192130 Available With Room
 
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
 
Cash Payment 9602870969 Escort Service in Udaipur Call Girls
Cash Payment 9602870969 Escort Service in Udaipur Call GirlsCash Payment 9602870969 Escort Service in Udaipur Call Girls
Cash Payment 9602870969 Escort Service in Udaipur Call Girls
 
Sales & Marketing Alignment: How to Synergize for Success
Sales & Marketing Alignment: How to Synergize for SuccessSales & Marketing Alignment: How to Synergize for Success
Sales & Marketing Alignment: How to Synergize for Success
 
Ensure the security of your HCL environment by applying the Zero Trust princi...
Ensure the security of your HCL environment by applying the Zero Trust princi...Ensure the security of your HCL environment by applying the Zero Trust princi...
Ensure the security of your HCL environment by applying the Zero Trust princi...
 

Imagecon 2019 - Aaron Gustafson

  • 1. Media in the Age of PWAs Aaron Gustafson @AaronGustafson slideshare.net/AaronGustafson
  • 2. What exactly is a Progressive Web App? © Brad Frost
  • 3. What exactly is a Progressive Web App? © Brad Frost
  • 4. “Progressive Web App” is a marketing term Progressive Web App © Brad Frost
  • 11. @AaronGustafson A Minimum Viable PWA HTTPS Web App Manifest
  • 12. @AaronGustafson Web App Manifest { "lang": "en", "short_name": "Wash Post", "name": "The Washington Post", "icons": [ { "src": "img/launcher-icon-2x.png", "sizes": "96x96", "type": "image/png" } ], "start_url": "/pwa/", "display": "standalone", "orientation": "portrait", "background_color": "black" }
  • 13. @AaronGustafson A Minimum Viable PWA HTTPS Web App Manifest Service Worker
  • 16. Carnival: 24% opt-in rate and 42% open rate for push notifications Katarzyna Ostrowska aka.ms/carnival-pwa
  • 17. Starbucks: 2x increase in daily active users aka.ms/google-io-2018
  • 18. Tinder: Core experience with 90% less code aka.ms/tinder-pwa-2017
  • 19. Trivago: 97% increase in click-outs to hotel offers aka.ms/trivago-pwa-2017
  • 20. West Elm: 15% increase in time on site 9% increase in revenue per visit aka.ms/west-elm-pwa-2017
  • 21. @AaronGustafson A Minimum Viable PWA HTTPS Web App Manifest Service Worker
  • 23. @AaronGustafson Registering a Service Worker if ( "serviceWorker" in navigator ) { navigator.serviceWorker.register( "/serviceworker.min.js" ); }
  • 24. @AaronGustafson Registering a Service Worker if ( "serviceWorker" in navigator ) { navigator.serviceWorker.register( "/serviceworker.min.js" ); }
  • 25. @AaronGustafson Registering a Service Worker if ( "serviceWorker" in navigator ) { navigator.serviceWorker.register( "/serviceworker.min.js" ); }
  • 26. @AaronGustafson Registering a Service Worker if ( "serviceWorker" in navigator ) { navigator.serviceWorker.register( "/serviceworker.min.js" ); } Path is important!
  • 27. @AaronGustafson The Service Worker Lifecycle Browser Install Activation Ready aka.ms/pwa-lifecycle
  • 28. @AaronGustafson How connections are made Browser Internet
  • 29. @AaronGustafson Along comes Service Worker Browser Internet
  • 30. @AaronGustafson Along comes Service Worker Browser Internet Cache
  • 31. @AaronGustafson Along comes Service Worker Browser Internet Cache !
  • 32. @AaronGustafson Know your (storage) limits Temporary Persistent Browser purges User purges
  • 33. @AaronGustafson Know your (storage) limits Volume Size Domain Limit Overall Limit ≤ 8 GB 20% of overall 50 MB 8–32 GB 500 MB 32–128 GB 4% of volume > 128 GB 4% or 20 GB
  • 34. Except on iOS. Safari gives you 50 MB.
  • 36. Storage is a privilege, don’t abuse it.
  • 37. How?
  • 38. @AaronGustafson Make good choices 1. No animated GIFs (especially as backgrounds) 38
  • 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
  • 63. @AaronGustafson Make good choices 8. Clean up after yourself 63 const version = "v2:", sw_caches = { static: { name: `${version}static` }, images: { name: `${version}images`, limit: 75 }, pages: { name: `${version}pages`, limit: 5 }, posts: { name: `${version}posts`, limit: 10 }, other: { name: `${version}other`, limit: 50 } }; 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