132 lines
4.5 KiB
JavaScript
132 lines
4.5 KiB
JavaScript
// Parse the parameters in the current script's own URL
|
||
const scriptUrl = new URL(self.location);
|
||
// Get the incoming version, and provide a default value if it doesn't exist
|
||
const version = scriptUrl.searchParams.get("v") || "default-version";
|
||
|
||
// Dynamically concatenate CACHE_NAME
|
||
const CACHE_NAME = `risk-guard-cache-${version}`;
|
||
|
||
console.log("Current Service Worker CACHE_NAME:", CACHE_NAME);
|
||
|
||
// 例如:umi.1a2b3c4d.js, logo.7b3e198f.png
|
||
const HASH_REGEX =
|
||
/\.[0-9a-f]{8}\.(js|css|png|jpe?g|gif|webp|svg|ico|woff2?|eot|ttf)$/i;
|
||
|
||
// 1. Installation phase: forcibly skip waiting and directly enter the activation state
|
||
self.addEventListener("install", (event) => {
|
||
self.skipWaiting();
|
||
});
|
||
|
||
// 2. Activation phase: clean up old caches and immediately take control of the page
|
||
self.addEventListener("activate", (event) => {
|
||
event.waitUntil(
|
||
caches.keys().then((cacheNames) => {
|
||
return Promise.all(
|
||
cacheNames.map((cache) => {
|
||
if (cache !== CACHE_NAME) {
|
||
console.log("Cleaning old cache:", cache);
|
||
return caches.delete(cache);
|
||
}
|
||
}),
|
||
);
|
||
}),
|
||
);
|
||
self.clients.claim(); // Immediately control all open clients (tabs)
|
||
});
|
||
|
||
// 3. Fetch event: this is the key to trigger the installation icon to be displayed
|
||
self.addEventListener("fetch", (event) => {
|
||
const { request } = event;
|
||
const url = new URL(request.url);
|
||
|
||
// If not a GET request, skip processing and go straight to the cache
|
||
if (request.method !== "GET") return;
|
||
|
||
// If not an http or https protocol (like chrome-extension), skip processing and go straight to the cache
|
||
if (!(url.protocol === "http:" || url.protocol === "https:")) {
|
||
return;
|
||
}
|
||
|
||
// Ignore HMR (hot module replacement) and API requests (you can adjust according to your actual API prefix)
|
||
if (
|
||
url.pathname.includes("/api/") ||
|
||
url.pathname.includes("/sockjs-node/")
|
||
) {
|
||
return;
|
||
}
|
||
|
||
// 1. Handle navigation requests: fetch from network first, then cache if successful
|
||
if (request.mode === "navigate") {
|
||
event.respondWith(
|
||
fetch(request).catch(() => {
|
||
return caches.match(request);
|
||
}),
|
||
);
|
||
return;
|
||
}
|
||
|
||
// 2. Handle static assets (js, css, images) - cache first
|
||
const isStaticAsset = url.pathname.match(
|
||
/\.(js|css|png|jpg|jpeg|svg|gif|woff2?|otf|ttf)$/,
|
||
);
|
||
|
||
const isHashed = HASH_REGEX.test(url.pathname);
|
||
|
||
if (isStaticAsset) {
|
||
if (isHashed) {
|
||
// ==========================================
|
||
// Strategy A: Resources with Hash -> Cache First
|
||
// ==========================================
|
||
event.respondWith(
|
||
caches.match(request).then((response) => {
|
||
// If cache hit, return the cached response immediately
|
||
if (response) return response;
|
||
|
||
// Otherwise, fetch from network first
|
||
return fetch(request).then((networkResponse) => {
|
||
if (
|
||
!networkResponse ||
|
||
networkResponse.status !== 200 ||
|
||
(networkResponse.type !== "basic" &&
|
||
networkResponse.type !== "cors")
|
||
) {
|
||
return networkResponse;
|
||
}
|
||
|
||
// Clone the response to allow multiple requests
|
||
const responseToCache = networkResponse.clone();
|
||
caches.open(CACHE_NAME).then((cache) => {
|
||
cache.put(request, responseToCache);
|
||
});
|
||
|
||
return networkResponse;
|
||
});
|
||
}),
|
||
);
|
||
} else {
|
||
// ==========================================
|
||
// Strategy B: Resources without Hash (such as index.html) -> Network First
|
||
// Ensure that the latest code is always retrieved, only fall back to cache when offline
|
||
// ==========================================
|
||
event.respondWith(
|
||
fetch(request)
|
||
.then((networkResponse) => {
|
||
// Network request successful, updating cache
|
||
if (networkResponse && networkResponse.status === 200) {
|
||
const responseToCache = networkResponse.clone();
|
||
caches.open(CACHE_NAME).then((cache) => {
|
||
cache.put(request, responseToCache);
|
||
});
|
||
}
|
||
return networkResponse;
|
||
})
|
||
.catch(() => {
|
||
// Network request failed (for example, the user disconnected from the Internet), try reading the historical version from the cache
|
||
console.log("[SW] Network offline, try reading cache:", request.url);
|
||
return caches.match(request);
|
||
}),
|
||
);
|
||
}
|
||
}
|
||
});
|