Skip to content

Instantly share code, notes, and snippets.

@radum
Last active November 25, 2019 11:49
Show Gist options
  • Save radum/5453919ae48163a8bda495fd91774d9e to your computer and use it in GitHub Desktop.
Save radum/5453919ae48163a8bda495fd91774d9e to your computer and use it in GitHub Desktop.
Request With Intent Caching Strategies in the age of PWAs
// KEEP A FALLBACK IMAGE IN YOUR BACK POCKET
// Assuming you want to use a fallback in more than one networking recipe,
// you can set up a named function that will respond with that resource:
function respondWithFallbackImage() {
return caches.match( "/i/fallbacks/offline.svg" );
}
// Then, within a fetch event handler, you can use that function to provide that fallback image
// when requests for images fail at the network:
self.addEventListener( "fetch", event => {
const request = event.request;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
return fetch( request, { mode: 'no-cors' } )
.then( response => {
return response;
})
.catch(
respondWithFallbackImage
);
);
}
});
// THE CACHING STRATEGY: PRIORITIZE CERTAIN MEDIA
// If you consider your media with this division in mind,
// you can establish some general guidelines for handling each, based on the situation.
// In other words, a caching strategy.
/**
* | Media category | Fast connection | Save-Data | Slow connection | No network |
* |----------------|------------------------------|--------------------------|-----------------|------------|
* | Critical | Load media | Replace with placeholder | | |
* | Nice-to-have | Load media | Replace with placeholder | | |
* | Non-critical | Remove from content entirely | | | |
*/
const high_priority = [
/aaron\-gustafson\.com/,
/adaptivewebdesign\.info/
];
// With that high_priority variable defined, I can create a function that will let me know
// if a given image request (for example) is a high priority request or not:
function isHighPriority( url ) {
// how many high priority links are we dealing with?
let i = high_priority.length;
// loop through each
while ( i-- ) {
// does the request URL match this regular expression?
if ( high_priority[i].test( url ) ) {
// yes, it’s a high priority request
return true;
}
}
// no matches, not high priority
return false;
}
// Adding support for prioritizing media requests only requires adding a new conditional
// into the fetch event handler, like we did with Save-Data.
// Your specific recipe for network and cache handling will likely differ,
// but here was how I chose to mix in this logic within image requests:
// Check the cache first
// Return the cached image if we have one
// If the image is not in the cache, continue
// Is this image high priority?
if ( isHighPriority( url ) ) {
// Fetch the image
// If the fetch succeeds, save a copy in the cache
// If not, respond with an "offline" placeholder
// Not high priority
} else {
// Should I save data?
if ( save_data ) {
// Respond with a "saving data" placeholder
// Not saving data
} else {
// Fetch the image
// If the fetch succeeds, save a copy in the cache
// If not, respond with an "offline" placeholder
}
}
// RESPECT A USER’S CHOICE TO SAVE DATA
// Some users reduce their data consumption by entering a “lite” mode or turning on a “data saver” feature.
// When this happens, browsers will often send a Save-Data header with their network requests.
// Within your Service Worker, you can look for this header and adjust your responses accordingly.
// First, you look for the header:
let save_data = false;
if ( 'connection' in navigator ) {
save_data = navigator.connection.saveData;
}
// Then, within your fetch handler for images, you might choose to preemptively respond with the fallback image
// instead of going to the network at all:
self.addEventListener( "fetch", event => {
const request = event.request;
if ( request.headers.get("Accept").includes("image") ) {
event.respondWith(
if ( save_data ) {
return respondWithFallbackImage();
}
// code you saw previously
);
}
});
// You could even take this a step further and tune respondWithFallbackImage()
// to provide alternate images based on what the original request was for.
// To do that you’d define several fallbacks globally in the Service Worker:
const fallback_avatar = "/i/fallbacks/avatar.svg",
fallback_image = "/i/fallbacks/image.svg";
// Both of those files should then be cached during the Service Worker install event:
return cache.addAll( [
fallback_avatar,
fallback_image
]);
// Finally, within respondWithFallbackImage() you could serve up the appropriate image based on the URL being fetched.
function respondWithFallbackImage( url ) {
const image = avatars.test( /webmention\.io/ ) ? fallback_avatar
: fallback_image;
return caches.match( image );
}
// With that change, I’ll need to update the fetch handler to pass in request.url
// as an argument to respondWithFallbackImage().
@radum
Copy link
Author

radum commented Nov 25, 2019

Request with Intent: Caching Strategies in the Age of PWAs

https://alistapart.com/article/request-with-intent-caching-strategies-in-the-age-of-pwas/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment