Files
Notes/main/file-list-cache.js
2026-10-16 16:34:15 +08:00

70 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// file:list 分页缓存(audit R7a P1-20)
//
// 旧版 file:list 一次性返回全部 entries + files,10000 文件 ≈ 1.5-2MB IPC
// payload,结构化克隆 + 渲染端 JSON.parse 都要 30-100ms,首次启动 cold paint
// 明显卡顿。改成「首次 200 + 分页」+ 模块级 cache:scan 一次切片多次返回,
// 避免 loadMore 反复触发全量 scanDir。
//
// 缓存失效:调用方在 file:write / file:create / file:delete / file:rename 成功
// 后调 invalidate(),下一次 file:list / file:list:more 重扫。简化策略:不做
// 精细 dirty entry 合并,整条缓存丢掉。scanDir 1-3s 是「极端大目录(OneDrive
// 1w 文件)」的最坏值,普通 < 100ms,重扫成本可控。
//
// 设计要点:
// - cache 严格按 dir 区分(Folder Browser 走 file:scan-dir 用 absDir,与根
// file:list 的 dataRoot 是不同 dir,互不污染)。
// - 不做 TTL:写操作主动失效是单一来源;fs-watcher 的 emitFilesChanged 不
// 影响 file:list cache(cache 只服务「分页拉取」,fs-watcher 走的是另
// 一条 IPC 通道)。
// - slice 边界由调用方保证(offset / limit 已经在 main.js file:list handler
// 里做过 safe* 夹紧);本模块只做"拿/建缓存 → 切片"。
/** @typedef {{ ok:true, dir:string, entries:Array } | { ok:false, error:string, code?:string, message?:string }} ScanResult */
/** 缓存项 */
let _cache = null; // { dir, entries, files, total, at }
/** 强制失效(写操作成功后调用) */
function invalidate() {
_cache = null;
}
/**
* 拉或建 file:list 缓存。dir 变化或缓存被显式失效时重扫。
* @param {string} dir - 绝对路径
* @param {(dir: string) => Promise<ScanResult>} scanFn - scanDir 注入(测试可换 mock)
* @returns {Promise<
* { dir:string, entries:Array, files:Array, total:number, at:number }
* | { error: ScanResult }
* >}
*/
async function getOrBuild(dir, scanFn) {
if (_cache && _cache.dir === dir) return _cache;
const result = await scanFn(dir);
if (!result.ok) return { error: result };
const entries = result.entries || [];
// files = 非文件夹条目(侧栏历史逻辑兼容:state.files 仅含文件)
const files = entries
.filter((e) => !e.isFolder)
.map((e) => ({ name: e.name, path: e.path, size: e.size, mtimeMs: e.mtimeMs, entryType: e.entryType }));
_cache = { dir, entries, files, total: entries.length, at: Date.now() };
return _cache;
}
/** 拿一份当前缓存的快照(不改)。test hook:单测断言缓存是否被命中/失效。 */
function peek() {
return _cache;
}
/** test hook:强制设缓存(不调 scanFn)。 */
function _setForTest(cache) {
_cache = cache;
}
module.exports = {
getOrBuild,
invalidate,
peek,
_setForTest,
};