-
-
Save NekR/d2d70f4e34d8829ea3c078456351ddce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const preloads = [ | |
'/main.js', | |
'/main.css', | |
'/logo.png' | |
]; | |
self.addEventListener('fetch', event => { | |
const request = event.request; | |
const url = new URL(request.url); | |
// Detect if it's a preflight request | |
if (request.mode === 'navigate' && request.headers.get('Service-Worker-Preflight')) { | |
let result; | |
// Note that self.request.match() should take into account | |
// existence of Service-Worker-Preflight header | |
// Case A: Construct the page on the fly (FB case) | |
if (url.pathname === '/newsfeed') { | |
result = self.requests.match(request).then(res => { | |
// `res` returns dynamic data, the shell is cached. | |
// Streaming rendering could be used here too | |
return constructThePage(res); | |
}); | |
// Preload assets in case response wasn't returned yet | |
preloadAssets(); | |
} | |
// Case B: Simple race with response for simple page | |
else if (url.pathname === '/about') { | |
const fetching = fetchRequest(request); | |
result = Promise.race([ | |
fetching, | |
self.requests.match(request).catch(() => fetching) | |
]); | |
} | |
// Case C: Canceling huge **response** | |
else if (url.pathname.endsWith('.mp4')) { | |
self.requests.match(request).then(res => { | |
res.body.cancel(); | |
}); | |
result = Response.error(); | |
} | |
event.respondWith(result); | |
return; | |
} | |
// Handle preloads, put them to the `self.requests` if aren't already there | |
if (preloads.indexOf(url.pathname) !== -1) { | |
// Check in existing requests | |
const result = self.requests.match(request).then(res => { | |
// If so, use it | |
if (res) return res; | |
// Check in caches next | |
return caches.match(request, { | |
cacheName: 'cache' | |
}).then(res => { | |
// If so, use it | |
if (res) return res; | |
// Fetch from the network and put to the cache | |
const fetching = fetch(request).then(res => { | |
if (res && res.ok) { | |
return putCache(request, res).then(() => res); | |
} | |
return res; | |
}); | |
// Put preloads to the store so it could be accessed if something, | |
// e.g. if this is preload request from `Link:` header and the asset | |
// will be requested from the page later | |
self.requests.putUntil(request, fetching); | |
return fetching; | |
}); | |
}); | |
event.respondWith(result); | |
return; | |
} | |
// Handle normal and preload requests. They are going here too | |
// and if page requests same preload assets which are now _ingligh_ | |
// then the same request is reused. | |
const result = self.requests.match(request).then(inflight => { | |
return inflight || self.caches.match(request); | |
}).then(res => { | |
if (res) return res; | |
return fetch(request).then(res => { | |
if (res && res.ok) { | |
putCache(request, res); | |
} | |
return res; | |
}); | |
}); | |
event.respondWith(result); | |
}); | |
function preloadAssets() { | |
preloads.forEach(asset => { | |
const req = new Request(asset); | |
self.requests.match(req).then(res => { | |
// If inflight -- ignore | |
if (res) return; | |
self.caches.match(req, { | |
cacheName: 'cache' | |
}).then(res => { | |
// If already preloaded -- ignore | |
if (res) return; | |
const fetching = fetch(req).then(res => { | |
if (res && res.ok) { | |
return putCache.then(() => res); | |
} | |
return res; | |
}); | |
self.requests.putUntil(req, fetching); | |
}); | |
}); | |
}); | |
} | |
function fetchRequest(request) { | |
return caches.match(request, { | |
cacheName: 'cache' | |
}).then(res => { | |
if (res) return res; | |
return fetch(request).then(res => { | |
if (res && res.ok) { | |
putCache(request, res); | |
} | |
return res; | |
}); | |
}); | |
} | |
function putCache(req, res) { | |
return caches.open('cache').then(cache => { | |
return cache.put(req, res); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment