cache loader data and document, network-first approach
This commit is contained in:
parent
8c0a6ccf7f
commit
5943044509
@ -1,21 +1,38 @@
|
|||||||
import type { FetchEventWithPreloadResponse } from "./fetch";
|
import { json } from "@remix-run/server-runtime";
|
||||||
|
|
||||||
export const ASSET_CACHE = "asset-cache";
|
export const ASSET_CACHE = "asset-cache";
|
||||||
|
export const DATA_CACHE = "data-cache";
|
||||||
|
export const DOCUMENT_CACHE = "document-cache";
|
||||||
|
|
||||||
export async function cacheAsset(event: FetchEventWithPreloadResponse) {
|
export function isAssetRequest(request: Request) {
|
||||||
|
return ["font", "image", "script", "style"].includes(request.destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLoaderRequest(request: Request) {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
return request.method.toLowerCase() === "get" && url.searchParams.get("_data");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDocumentGetRequest(request: Request) {
|
||||||
|
return request.method.toLowerCase() === "get" && request.mode === "navigate";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cacheAsset(event: FetchEvent) {
|
||||||
|
// stale-while-revalidate
|
||||||
const url = new URL(event.request.url);
|
const url = new URL(event.request.url);
|
||||||
const cachedResponse = await caches.match(event.request, {
|
return caches
|
||||||
|
.match(event.request, {
|
||||||
cacheName: ASSET_CACHE,
|
cacheName: ASSET_CACHE,
|
||||||
ignoreVary: true,
|
ignoreVary: true,
|
||||||
ignoreSearch: true,
|
ignoreSearch: true,
|
||||||
});
|
})
|
||||||
|
.then((cachedResponse) => {
|
||||||
console.debug(`Serving asset from ${cachedResponse ? "cache" : " network"}`, url.pathname);
|
console.debug(`Serving asset from ${cachedResponse ? "cache" : " network"}`, url.pathname);
|
||||||
|
|
||||||
const fetchPromise = (async () => {
|
const fetchPromise = event.preloadResponse
|
||||||
const cache = await caches.open(ASSET_CACHE);
|
.then((preloadedResponse?: Response) => preloadedResponse || fetch(event.request.clone()))
|
||||||
const preloadedResponse = await event.preloadResponse;
|
.then((response) =>
|
||||||
const response = preloadedResponse || (await fetch(event.request));
|
caches.open(ASSET_CACHE).then((cache) => {
|
||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 200:
|
case 200:
|
||||||
cache.put(event.request, response.clone());
|
cache.put(event.request, response.clone());
|
||||||
@ -26,7 +43,64 @@ export async function cacheAsset(event: FetchEventWithPreloadResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
})();
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return cachedResponse || fetchPromise;
|
return cachedResponse || fetchPromise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cacheLoaderData(event: FetchEvent) {
|
||||||
|
// network-first
|
||||||
|
const url = new URL(event.request.url);
|
||||||
|
console.debug("Serving data from network", url.pathname + url.search);
|
||||||
|
|
||||||
|
return event.preloadResponse
|
||||||
|
.then((preloadedResponse?: Response) => preloadedResponse || fetch(event.request.clone()))
|
||||||
|
.then((response) =>
|
||||||
|
caches
|
||||||
|
.open(DATA_CACHE)
|
||||||
|
.then((cache) => cache.put(event.request, response.clone()))
|
||||||
|
.then(() => response),
|
||||||
|
)
|
||||||
|
.catch(() => {
|
||||||
|
console.debug("Serving data from network failed, falling back to cache", url.pathname + url.search);
|
||||||
|
return caches.match(event.request).then((response) => {
|
||||||
|
if (!response) {
|
||||||
|
return json(
|
||||||
|
{ message: "Network Error" },
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "X-Remix-Catch": "yes", "X-Remix-Worker": "yes" },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.headers.set("X-Remix-Worker", "yes");
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cacheDocument(event: FetchEvent): Promise<Response> {
|
||||||
|
// network-first
|
||||||
|
const url = new URL(event.request.url);
|
||||||
|
console.debug("Serving document from network", url.pathname);
|
||||||
|
return caches.open(DOCUMENT_CACHE).then((cache) =>
|
||||||
|
fetch(event.request.clone())
|
||||||
|
.then((response) => {
|
||||||
|
cache.put(event.request, response.clone());
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.debug("Serving document from network failed, falling back to cache", url.pathname);
|
||||||
|
return caches.match(event.request).then((response) => {
|
||||||
|
if (!response) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,26 @@
|
|||||||
import { ASSET_CACHE, cacheAsset } from "~/service-worker/cache-utils";
|
import {
|
||||||
|
cacheAsset,
|
||||||
|
cacheDocument,
|
||||||
|
cacheLoaderData,
|
||||||
|
isAssetRequest,
|
||||||
|
isDocumentGetRequest,
|
||||||
|
isLoaderRequest,
|
||||||
|
} from "~/service-worker/cache-utils";
|
||||||
|
|
||||||
declare let self: ServiceWorkerGlobalScope;
|
declare let self: ServiceWorkerGlobalScope;
|
||||||
|
|
||||||
export type FetchEventWithPreloadResponse = FetchEvent & { preloadResponse?: Promise<Response | undefined> };
|
export default async function handleFetch(event: FetchEvent) {
|
||||||
|
if (isAssetRequest(event.request)) {
|
||||||
export default async function handleFetch(event: FetchEventWithPreloadResponse) {
|
|
||||||
if (["font", "image", "script", "style"].includes(event.request.destination)) {
|
|
||||||
return cacheAsset(event);
|
return cacheAsset(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLoaderRequest(event.request)) {
|
||||||
|
return cacheLoaderData(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDocumentGetRequest(event.request)) {
|
||||||
|
return cacheDocument(event);
|
||||||
|
}
|
||||||
|
|
||||||
return fetch(event.request);
|
return fetch(event.request);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user