perf(canvas): 大画布渲染与媒体 - 布局不变时复用空间索引,合并重复的资源与封面请求

连线索引只存端点 ID,文本更新不再重建几何。小地图一次遍历隐藏折叠帧子节点。资源 Blob 与签名刷新合并在途请求,视频封面优先复用已缓存文件。
This commit is contained in:
ddcat666 committed 2026-10-09 17:17:38 +08:00
1 parent 6938b145ba
commit 4175d82dc9
10 files changed
+286 -107

No files matched your search

+29 -35
View File
@@ -17,7 +17,7 @@ const blobStore = localforage.createInstance({ name: "yingce", storeName: "resou
const metaStore = localforage.createInstance({ name: "yingce", storeName: "resource_blob_meta" });
const objectUrls = new Map<string, string>();
const sessionBlobs = new Map<string, Blob>();
const inFlight = new Map<string, Promise<string>>();
const blobInFlight = new Map<string, Promise<Blob | null>>();
const cacheMetaTouchWarnings = new Set<string>();
const metaTouchedAt = new Map<string, number>();
const downloadQueue: Array<() => void> = [];
@@ -39,7 +39,7 @@ export function clearResourceBlobCache() {
objectUrls.forEach((url) => URL.revokeObjectURL(url));
objectUrls.clear();
sessionBlobs.clear();
inFlight.clear();
blobInFlight.clear();
metaTouchedAt.clear();
cacheMetaTouchWarnings.clear();
cacheStats = null;
@@ -65,16 +65,9 @@ export async function cacheResourceObjectUrl(storageKey: string) {
if (!target) return "";
const cached = await readCachedObjectUrl(target);
if (cached) return cached;
const pending = inFlight.get(target.key);
if (pending) return pending;
const generation = cacheGeneration;
let task: Promise<string>;
task = withDownloadSlot(() => downloadAndCacheResource(storageKey, target, generation)).finally(() => {
if (inFlight.get(target.key) === task) inFlight.delete(target.key);
});
inFlight.set(target.key, task);
return task;
const blob = await getOrDownloadResourceBlob(storageKey, target);
return blob ? objectUrl(target.key, blob) : "";
}
function withDownloadSlot<T>(task: () => Promise<T>) {
@@ -108,41 +101,36 @@ export async function primeResourceBlobCache(storageKey: string, blob: Blob) {
export async function getCachedResourceBlob(storageKey: string) {
const target = await cacheTarget(storageKey);
if (!target) return null;
const sessionBlob = sessionBlobs.get(target.key);
if (sessionBlob) return sessionBlob;
const cached = await blobStore.getItem<Blob>(target.key);
if (cached) {
sessionBlobs.set(target.key, cached);
touchCacheMetaSafely(target);
return cached;
}
const sessionBlob = sessionBlobs.get(target.key);
if (sessionBlob) return sessionBlob;
const pending = inFlight.get(target.key);
if (pending) {
await pending.catch(() => "");
const downloaded = sessionBlobs.get(target.key) || (await blobStore.getItem<Blob>(target.key));
if (downloaded) return downloaded;
return loadAndPersistResource(storageKey);
}
await cacheResourceObjectUrl(storageKey).catch(() => "");
const downloaded = sessionBlobs.get(target.key) || (await blobStore.getItem<Blob>(target.key));
if (downloaded) return downloaded;
return loadAndPersistResource(storageKey);
}
async function loadAndPersistResource(storageKey: string) {
const blob = await getResourceBlob(storageKey);
if (blob) await primeResourceBlobCache(storageKey, blob).catch(() => "");
const blob = await getOrDownloadResourceBlob(storageKey, target);
if (blob) objectUrl(target.key, blob);
return blob;
}
async function downloadAndCacheResource(storageKey: string, target: ResourceCacheMeta, generation: number) {
const blob = await downloadResourceBlob(storageKey, target);
if (!blob || generation !== cacheGeneration) return "";
return objectUrl(target.key, blob);
async function getOrDownloadResourceBlob(storageKey: string, target: ResourceCacheMeta) {
const sessionBlob = sessionBlobs.get(target.key);
if (sessionBlob) return sessionBlob;
const pending = blobInFlight.get(target.key);
if (pending) return pending;
const generation = cacheGeneration;
const task = withDownloadSlot(() => downloadResourceBlob(storageKey, target, generation)).finally(() => {
if (blobInFlight.get(target.key) === task) blobInFlight.delete(target.key);
});
blobInFlight.set(target.key, task);
return task;
}
async function downloadResourceBlob(storageKey: string, target: ResourceCacheMeta) {
async function downloadResourceBlob(storageKey: string, target: ResourceCacheMeta, generation: number) {
const blob = await getResourceBlob(storageKey);
if (!blob) return null;
if (!blob || generation !== cacheGeneration) return null;
sessionBlobs.set(target.key, blob);
if (blob.size <= MAX_CACHE_BYTES) await enqueuePersist(target, blob);
return blob;
@@ -209,8 +197,14 @@ async function readCachedObjectUrl(target: ResourceCacheMeta) {
touchCacheMetaSafely(target);
return existing;
}
const sessionBlob = sessionBlobs.get(target.key);
if (sessionBlob) {
touchCacheMetaSafely(target);
return objectUrl(target.key, sessionBlob);
}
const blob = await blobStore.getItem<Blob>(target.key);
if (!blob) return "";
sessionBlobs.set(target.key, blob);
touchCacheMetaSafely(target);
return objectUrl(target.key, blob);
}