Improve WebGL asset caching

This commit is contained in:
yangbear
2026-07-14 02:03:44 +08:00
parent 53f856e44e
commit 53fe281ecc
3 changed files with 67 additions and 6 deletions
+21
View File
@@ -15,11 +15,32 @@ self.addEventListener('install', function (e) {
const cache = await caches.open(cacheName);
console.log('[Service Worker] Caching all: app shell and content');
await cache.addAll(contentToCache);
await self.skipWaiting();
})());
});
self.addEventListener('activate', function (e) {
e.waitUntil((async function () {
const keys = await caches.keys();
await Promise.all(keys.map(function (key) {
return key === cacheName ? Promise.resolve() : caches.delete(key);
}));
await self.clients.claim();
})());
});
self.addEventListener('fetch', function (e) {
if (e.request.method !== 'GET') {
return;
}
e.respondWith((async function () {
const url = new URL(e.request.url);
const isBuildAsset = url.pathname.includes('/Build/') || url.pathname.includes('/StreamingAssets/');
if (!isBuildAsset && !url.pathname.endsWith('/TemplateData/style.css')) {
return fetch(e.request);
}
let response = await caches.match(e.request);
console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
if (response) { return response; }
+7
View File
@@ -69,6 +69,13 @@
showBanner: unityShowBanner,
};
config.cacheControl = function (url) {
if (url === config.dataUrl || url === config.frameworkUrl || url === config.codeUrl) {
return "immutable";
}
return "must-revalidate";
};
// By default Unity keeps WebGL canvas render target size matched with
// the DOM size of the canvas element (scaled by window.devicePixelRatio)
// Set this to false if you want to decouple this synchronization from
+39 -6
View File
@@ -36,8 +36,11 @@ function originalCompressedPath(fp) {
}
function headersForFile(fp, size, encoding) {
const originalPath = originalCompressedPath(fp);
const fileName = path.basename(originalPath);
const isHashedBuildAsset = /\/Build\/[a-f0-9]{32}\./.test(fp);
const headers = {
'Content-Type': extType(originalCompressedPath(fp)),
'Content-Type': extType(originalPath),
'Content-Length': size,
'X-Content-Type-Options': 'nosniff',
'Accept-Ranges': 'bytes',
@@ -45,11 +48,15 @@ function headersForFile(fp, size, encoding) {
if (encoding != null) {
headers['Content-Encoding'] = encoding;
headers['Cache-Control'] = 'public, max-age=604800';
headers['Cache-Control'] = isHashedBuildAsset
? 'public, max-age=31536000, immutable'
: 'public, max-age=604800';
} else if (/\.(mp4|m4v|webm|ogg)$/i.test(fp)) {
headers['Cache-Control'] = 'public, max-age=604800';
} else if (fp.endsWith('.loader.js')) {
headers['Cache-Control'] = 'public, max-age=31536000, immutable';
} else if (fileName === 'ServiceWorker.js' || fileName === 'index.html' || fileName === 'manifest.webmanifest') {
headers['Cache-Control'] = 'no-cache';
} else if (isHashedBuildAsset) {
headers['Cache-Control'] = 'public, max-age=31536000, immutable';
} else {
headers['Cache-Control'] = 'public, max-age=3600';
}
@@ -57,6 +64,24 @@ function headersForFile(fp, size, encoding) {
return headers;
}
function addValidators(headers, stat) {
headers['ETag'] = `"${stat.size.toString(16)}-${Math.floor(stat.mtimeMs).toString(16)}"`;
headers['Last-Modified'] = stat.mtime.toUTCString();
return headers;
}
function isFresh(req, headers) {
const ifNoneMatch = req.headers['if-none-match'];
if (ifNoneMatch && ifNoneMatch === headers['ETag']) return true;
const ifModifiedSince = req.headers['if-modified-since'];
if (ifModifiedSince && headers['Last-Modified']) {
return new Date(ifModifiedSince).getTime() >= new Date(headers['Last-Modified']).getTime();
}
return false;
}
function detectUnityEncoding(fp, callback) {
if (fp.endsWith('.gz')) {
callback(null, 'gzip');
@@ -242,14 +267,22 @@ function serveStatic(req, res) {
return;
}
const headers = headersForFile(fp, end - start + 1, encoding);
const headers = addValidators(headersForFile(fp, end - start + 1, encoding), stat);
headers['Content-Range'] = `bytes ${start}-${end}/${stat.size}`;
res.writeHead(206, headers);
fs.createReadStream(fp, { start, end }).pipe(res);
return;
}
res.writeHead(200, headersForFile(fp, stat.size, encoding));
const headers = addValidators(headersForFile(fp, stat.size, encoding), stat);
if (isFresh(req, headers)) {
delete headers['Content-Length'];
res.writeHead(304, headers);
res.end();
return;
}
res.writeHead(200, headers);
fs.createReadStream(fp).pipe(res);
});
});