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[]> {
|
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) {
|
if (raw === null) {
|
||||||
await openCache()
|
await openPreCache()
|
||||||
return getPreCachedUrls()
|
return getPreCachedUrls()
|
||||||
}
|
}
|
||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCache(): Promise<Cache> {
|
async function getPreCache(): Promise<Cache> {
|
||||||
const version = await swStorage.getItem<string>('$sw.cache.version')
|
const version = await swStorage.getItem<string>('$sw.pre-cache.version')
|
||||||
|
// Get cache from server
|
||||||
if (version === null) {
|
if (version === null) {
|
||||||
await openCache()
|
await openPreCache()
|
||||||
return getCache()
|
return getPreCache()
|
||||||
}
|
}
|
||||||
return caches.open(version)
|
return caches.open(version)
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +54,7 @@ const IS_SW = 'onpushsubscriptionchange' in self
|
||||||
if (IS_SW) {
|
if (IS_SW) {
|
||||||
self.addEventListener('install', (event) => {
|
self.addEventListener('install', (event) => {
|
||||||
// Assign global cache and pre-cached-urls
|
// Assign global cache and pre-cached-urls
|
||||||
event.waitUntil(openCache())
|
event.waitUntil(openPreCache())
|
||||||
})
|
})
|
||||||
|
|
||||||
self.addEventListener('activate', () => {
|
self.addEventListener('activate', () => {
|
||||||
|
|
@ -85,18 +87,22 @@ if (IS_SW) {
|
||||||
async function fetchHandler(event: FetchEvent) {
|
async function fetchHandler(event: FetchEvent) {
|
||||||
const url = new URL(event.request.url)
|
const url = new URL(event.request.url)
|
||||||
const method = event.request.method
|
const method = event.request.method
|
||||||
|
|
||||||
const preCachedUrls = await getPreCachedUrls()
|
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"
|
// Cache first for "pre-cached-urls"
|
||||||
if (preCachedUrls.includes(url.pathname) && method === 'GET') {
|
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) =>
|
await fetch(event.request).then((response) =>
|
||||||
response.ok ? response : null
|
response.ok ? response : null
|
||||||
).catch(() => null) ??
|
).catch(() => null) ??
|
||||||
await cache.match(event.request, { ignoreSearch: true })
|
await preCache.match(event.request, { ignoreSearch: true })
|
||||||
|
|
||||||
if (cached === undefined) {
|
if (cached === undefined) {
|
||||||
throw new Error(`no cache available for pre-cached-url "${url}"`)
|
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())
|
cache.put(event.request, response.clone())
|
||||||
return response
|
return response
|
||||||
})
|
})
|
||||||
.catch((cause) => {
|
async function getDynCache(): Promise<Cache> {
|
||||||
// Try serve cache if no network
|
const version = await swStorage.getItem<number>('$sw.dyn-cache.version')
|
||||||
if (cached === undefined) {
|
// Create cache
|
||||||
throw new Error(`no cache available for "${url}"`, { cause })
|
if (version === null) {
|
||||||
}
|
await swStorage.setItem<number>('$sw.dyn-cache.version', now + lifeTime)
|
||||||
return cached
|
return getDynCache()
|
||||||
})
|
|
||||||
|
|
||||||
if (cached === undefined) {
|
|
||||||
return response
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cached
|
// 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
|
// Network only
|
||||||
return fetch(event.request)
|
return fetch(event.request)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateCache() {
|
const serverVersion = await getServerPreCacheVersion()
|
||||||
const serverVersion = await getServerCacheVersion()
|
const clientVersion = await swStorage.getItem<string>('$sw.pre-cache.version')
|
||||||
const clientVersion = await swStorage.getItem<string>('$sw.cache.version')
|
|
||||||
if (clientVersion === null) return
|
if (clientVersion === null) return
|
||||||
if (serverVersion === undefined) return
|
if (serverVersion === undefined) return
|
||||||
if (clientVersion === serverVersion) return
|
if (clientVersion === serverVersion) return
|
||||||
|
|
||||||
// Open new pre-cache
|
// Open new pre-cache
|
||||||
await openCache()
|
await openPreCache()
|
||||||
// Delete old pre-cache
|
// Delete old pre-cache
|
||||||
caches.delete(clientVersion)
|
caches.delete(clientVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getServerCacheVersion() {
|
async function getServerPreCacheVersion(): Promise<string | undefined> {
|
||||||
const response = await fetch('/api/serviceworker/precache').then(
|
const response = await fetch('/api/serviceworker/precache').then(
|
||||||
(response) => response.json() as Promise<ApiPayload<PrecacheResponse>>,
|
(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(
|
const response = await fetch('/api/serviceworker/precache').then(
|
||||||
(response) => response.json() as Promise<ApiPayload<PrecacheResponse>>,
|
(response) => response.json() as Promise<ApiPayload<PrecacheResponse>>,
|
||||||
)
|
)
|
||||||
|
|
@ -183,8 +191,8 @@ async function openCache() {
|
||||||
}
|
}
|
||||||
await Promise.allSettled(addList)
|
await Promise.allSettled(addList)
|
||||||
|
|
||||||
await swStorage.setItem('$sw.cache.version', version)
|
await swStorage.setItem('$sw.pre-cache.version', version)
|
||||||
await swStorage.setItem('$sw.cache.urls', preCachedUrls)
|
await swStorage.setItem('$sw.pre-cache.urls', preCachedUrls)
|
||||||
|
|
||||||
return { cache, version, preCachedUrls }
|
return { cache, version, preCachedUrls }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue