feat(pwa): ⚡ split pre-cache and dynamic cache in service worker
This commit is contained in:
parent
18f911b79b
commit
4a917971d0
|
|
@ -31,19 +31,21 @@ const swStorage = {
|
|||
}
|
||||
|
||||
async function getPreCachedUrls(): Promise<string[]> {
|
||||
const raw = await swStorage.getItem<string[]>('$sw.cache.urls')
|
||||
const raw = await swStorage.getItem<string[]>('$sw.pre-cache.urls')
|
||||
|
||||
if (raw === null) {
|
||||
await openCache()
|
||||
await openPreCache()
|
||||
return getPreCachedUrls()
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
async function getCache(): Promise<Cache> {
|
||||
const version = await swStorage.getItem<string>('$sw.cache.version')
|
||||
async function getPreCache(): Promise<Cache> {
|
||||
const version = await swStorage.getItem<string>('$sw.pre-cache.version')
|
||||
// Get cache from server
|
||||
if (version === null) {
|
||||
await openCache()
|
||||
return getCache()
|
||||
await openPreCache()
|
||||
return getPreCache()
|
||||
}
|
||||
return caches.open(version)
|
||||
}
|
||||
|
|
@ -52,7 +54,7 @@ const IS_SW = 'onpushsubscriptionchange' in self
|
|||
if (IS_SW) {
|
||||
self.addEventListener('install', (event) => {
|
||||
// Assign global cache and pre-cached-urls
|
||||
event.waitUntil(openCache())
|
||||
event.waitUntil(openPreCache())
|
||||
})
|
||||
|
||||
self.addEventListener('activate', () => {
|
||||
|
|
@ -85,18 +87,22 @@ if (IS_SW) {
|
|||
async function fetchHandler(event: FetchEvent) {
|
||||
const url = new URL(event.request.url)
|
||||
const method = event.request.method
|
||||
|
||||
const preCachedUrls = await getPreCachedUrls()
|
||||
const cache = await getCache()
|
||||
// Cache will be updated on each request if version change
|
||||
updateCache()
|
||||
|
||||
// Cache first for "pre-cached-urls"
|
||||
if (preCachedUrls.includes(url.pathname) && method === 'GET') {
|
||||
const cached = await cache.match(event.request) ??
|
||||
const preCache = await getPreCache()
|
||||
|
||||
// Cache request update on each request if version change
|
||||
updatePreCache()
|
||||
|
||||
// TODO handle search params
|
||||
const cached = await preCache.match(event.request) ??
|
||||
await fetch(event.request).then((response) =>
|
||||
response.ok ? response : null
|
||||
).catch(() => null) ??
|
||||
await cache.match(event.request, { ignoreSearch: true })
|
||||
await preCache.match(event.request, { ignoreSearch: true })
|
||||
|
||||
if (cached === undefined) {
|
||||
throw new Error(`no cache available for pre-cached-url "${url}"`)
|
||||
|
|
@ -119,39 +125,41 @@ async function fetchHandler(event: FetchEvent) {
|
|||
cache.put(event.request, response.clone())
|
||||
return response
|
||||
})
|
||||
.catch((cause) => {
|
||||
// Try serve cache if no network
|
||||
if (cached === undefined) {
|
||||
throw new Error(`no cache available for "${url}"`, { cause })
|
||||
}
|
||||
return cached
|
||||
})
|
||||
|
||||
if (cached === undefined) {
|
||||
return response
|
||||
}
|
||||
|
||||
return cached
|
||||
async function getDynCache(): Promise<Cache> {
|
||||
const version = await swStorage.getItem<number>('$sw.dyn-cache.version')
|
||||
// Create cache
|
||||
if (version === null) {
|
||||
await swStorage.setItem<number>('$sw.dyn-cache.version', now + lifeTime)
|
||||
return getDynCache()
|
||||
}
|
||||
|
||||
// Clean outdated cache
|
||||
if (version < Date.now()) {
|
||||
await swStorage.setItem<number>('$sw.dyn-cache.version', now + lifeTime)
|
||||
await caches.delete(version.toString())
|
||||
return getDynCache()
|
||||
}
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
// Network only
|
||||
return fetch(event.request)
|
||||
}
|
||||
|
||||
async function updateCache() {
|
||||
const serverVersion = await getServerCacheVersion()
|
||||
const clientVersion = await swStorage.getItem<string>('$sw.cache.version')
|
||||
const serverVersion = await getServerPreCacheVersion()
|
||||
const clientVersion = await swStorage.getItem<string>('$sw.pre-cache.version')
|
||||
if (clientVersion === null) return
|
||||
if (serverVersion === undefined) return
|
||||
if (clientVersion === serverVersion) return
|
||||
|
||||
// Open new pre-cache
|
||||
await openCache()
|
||||
await openPreCache()
|
||||
// Delete old pre-cache
|
||||
caches.delete(clientVersion)
|
||||
}
|
||||
|
||||
async function getServerCacheVersion() {
|
||||
async function getServerPreCacheVersion(): Promise<string | undefined> {
|
||||
const response = await fetch('/api/serviceworker/precache').then(
|
||||
(response) => response.json() as Promise<ApiPayload<PrecacheResponse>>,
|
||||
)
|
||||
|
|
@ -161,7 +169,7 @@ async function getServerCacheVersion() {
|
|||
}
|
||||
}
|
||||
|
||||
async function openCache() {
|
||||
async function openPreCache() {
|
||||
const response = await fetch('/api/serviceworker/precache').then(
|
||||
(response) => response.json() as Promise<ApiPayload<PrecacheResponse>>,
|
||||
)
|
||||
|
|
@ -183,8 +191,8 @@ async function openCache() {
|
|||
}
|
||||
await Promise.allSettled(addList)
|
||||
|
||||
await swStorage.setItem('$sw.cache.version', version)
|
||||
await swStorage.setItem('$sw.cache.urls', preCachedUrls)
|
||||
await swStorage.setItem('$sw.pre-cache.version', version)
|
||||
await swStorage.setItem('$sw.pre-cache.urls', preCachedUrls)
|
||||
|
||||
return { cache, version, preCachedUrls }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue