鸿蒙 PC Markdown 编辑器目录树性能:惰性展开与状态保持

📅 2026/7/20 14:05:41
鸿蒙 PC Markdown 编辑器目录树性能:惰性展开与状态保持
鸿蒙 PC Markdown 编辑器目录树性能惰性展开与状态保持文件树性能不是把 ArkUI List换成更快组件就结束。真正成本来自目录枚举、每项 stat、URI构造、排序、节点数量、展开状态和外部变化。一个根目录可能只有十项也可能一层包含数千文件递归扫描会让用户只想打开 README却等待整个仓库。本文基于 OhMarkdown分析非递归读取、两千项上限、扁平可见数组、展开插入与折叠删除并说明当前缓存和刷新边界。代码位于 https://gitcode.com/VON-/codex_md_oh首屏只读取一级constlistedNamesawaitfileIo.listFile(directoryPath,{recursion:false,listNum:MAX_DIRECTORY_ENTRIES1});recursion: false是最重要的性能决策。根目录打开成本只与直接子项相关深层归档、依赖和图片不会提前遍历。用户点击某目录时才调用同一服务读取下一层。递归读取不仅慢还会把某个深层权限错误升级为整个工作区打开失败并增加循环链接风险。惰性展开让错误局部化到节点。用 MAX1 判断截断constMAX_DIRECTORY_ENTRIES2000;consttruncatedlistedNames.lengthMAX_DIRECTORY_ENTRIES;constvisibleNameslistedNames.slice(0,MAX_DIRECTORY_ENTRIES).filter(isSafeEntryName);请求2001项是为了知道是否还有更多。只请求2000项返回满额时无法区分“恰好2000”和“被截断”。UI显示 first 2000 entries shown不无声遗漏。上限控制后续 Promise、stat、排序和 List数据规模。数值是 Alpha保护边界尚未在设备生成两千项语料做完整压力测试因此报告必须标为代码保护而非规模实测通过。类型查询是主要 I/O 成本每个安全名称需要 statconstentriesawaitPromise.all(visibleNames.map(async(name){constchildUricreateChildUri(directoryUri,name);conststatawaitfileIo.stat(childUri);constisDirectorystat.isDirectory();if(!isDirectory!isMarkdownDocument(name)){returnundefined;}return{name,uri:childUri,parentUri:directoryUri,isDirectory,depth,expanded:false};}));Promise.all让本地 I/O并发但两千个 stat可能形成峰值。未来应测不同 provider云盘或网络文件系统可能不适合无界并发。可以用固定并发池逐批32项在延迟和资源之间平衡。如果平台目录 API能直接返回类型可减少 stat但替换前要验证 URI授权与 provider兼容不应只为少一行调用改变边界。过滤减少可见节点目录始终保留普通文件只显示.md、.markdown、.mdown、.mkd和.txt。图片和构建产物不进入 List既符合当前编辑能力也减少 UI项。过滤发生在 stat后因为先要知道同名后缀对象是不是目录。仅靠扩展名可能把notes.md目录当文件。未来显示资源文件时应引入类型分类而不是取消所有过滤。排序在服务层完成filteredEntries.sort((left,right){if(left.isDirectory!right.isDirectory){returnleft.isDirectory?-1:1;}returnleft.name.localeCompare(right.name);});目录优先、同类按名称。服务返回即为最终展示顺序ArkUI重绘不重复排序。稳定 URI作为 ForEach key插入子项不会让未变化行失去身份。localeCompare符合系统语言习惯但跨设备顺序可能不同。若测试依赖固定顺序应使用明确 locale或只断言目录优先和集合内容。扁平数组比递归组件更直接当前可见树是一维workspaceEntries。每项有 depth和 expanded父节点后紧跟所有可见后代。ArkUI List直接迭代ForEach(this.workspaceEntries,(entry:WorkspaceEntry){ListItem(){this.workspaceEntryRow(entry)}},(entry:WorkspaceEntry):stringentry.uri)扁平模型适合虚拟 List滚动索引明确不需要递归组件树。缩进由 depth计算URI为稳定 key。代价是展开和折叠需要数组切片复杂度 O(n)。最多两千可见项时可接受若未来工作区达到数万可见节点需要分页、虚拟树或增量数据结构。展开只插入直接子项constchildWorkspaceawaitlistWorkspaceDirectory(entry.uri,entry.depth1);constexpandedEntry{...entry,expanded:true};this.workspaceEntriesthis.workspaceEntries.slice(0,entryIndex).concat([expandedEntry],childWorkspace.entries,this.workspaceEntries.slice(entryIndex1));读成功后才把父节点标为 expanded。失败时旧数组不变用户可以重试。子项 depth为父加一不递归加载孙节点。操作期间operationInProgresstrue防止双击产生两个并发读取并重复插入。它是全工作台互斥简单但会阻止同时打开另一个文件未来可改为按 URI加载集合。折叠删除连续后代letremoveEndentryIndex1;while(removeEndthis.workspaceEntries.lengththis.workspaceEntries[removeEnd].depthentry.depth){removeEnd1;}this.workspaceEntriesthis.workspaceEntries.slice(0,entryIndex).concat([{...entry,expanded:false}],this.workspaceEntries.slice(removeEnd));深度优先可见顺序保证后代连续扫描到同级或更浅节点结束。整个子树一次移除。折叠会丢弃子节点缓存再展开重新读取因此能看到部分外部变化也增加重复 I/O。如果要保留状态可建立 URI到子项缓存折叠只隐藏但缓存需要失效策略。目录外部变化、权限撤销和重命名都会使旧结果过期。当前选择重新读取优先正确性。展开状态的当前含义expanded只存在运行时可见数组。应用重启、重新选择工作区或折叠后重新展开都不会恢复深层展开状态。这不是功能缺陷伪装而是当前边界。要持久化展开状态应保存 URI集合而非数组索引。重开工作区后按层逐步恢复并设置最大恢复深度和超时避免启动重新扫描大量目录。不存在的 URI要静默移除。错误反馈不清空已有树目录读取失败只设置this.operationStatusFolder failed:${errorinstanceofError?error.message:String(error)};父节点保持折叠其他节点仍可用。局部故障不应清空整个工作区。截断则使用明确状态和根面板提示不把它当异常弹框。后续可在失败节点旁显示重试图标比全局状态栏更接近问题位置。错误文案要避免暴露完整私有路径。鸿蒙 PC 实际树下图来自 MateBook Pro 2in1模拟器目录展开后插入嵌套 Markdown文件树仍保持紧凑的一维列表。模拟器已经验证一级读取、按需展开、折叠和树内打开。规模测试还应生成宽目录、深目录、空目录、无权限目录、大小写重名和大量非 Markdown文件分别记录打开耗时、内存和滚动帧率。可观测指标目录性能不能只记录“打开成功”。建议记录选择器返回到首屏树可见的耗时、listFile耗时、stat总量、过滤后节点数、截断标记、展开耗时和失败类型。日志只记录数量与毫秒不记录用户名称和 URI。对两千项目录应分别测本地 Documents、外部存储和云 provider。Promise并发策略以 P95延迟和内存为依据。ArkUI List滚动还需用帧率和卡顿判断不能由服务耗时推断。文件监听与刷新当前没有实时监听。外部新增文件在已展开节点中不会立刻出现折叠再展开会刷新。下一步可先增加手动刷新保存展开 URI重新加载可见层。实时监听要处理事件合并、重命名、父目录删除和应用后台。监听不能绕过两千项上限也不能把每个事件直接触发全树扫描。可将事件按目录聚合在动画或短防抖窗口后刷新目标层。当前边界全局互斥限制并发操作stat并发未分批折叠不缓存展开状态不持久化没有刷新与监听两千项尚缺设备规模实测符号链接和循环语义未完整验证。这些边界已经被限制在服务与树模型内不影响文档会话和保存。后续优化可以替换目录加载策略而无需改编辑器内核。结语目录树性能来自控制工作量非递归首屏、MAX1截断判断、类型过滤、稳定排序、扁平可见数组、按需插入和整段折叠。它不是无限扫描后期待 List虚拟化解决一切。鸿蒙 PC Markdown编辑器首先保证小工作区立即可用、大目录有边界、局部失败可恢复。缓存、监听和更高规模必须建立在真实 provider数据上逐步增加。