Skip to content

第 19 章 · 电梯的楼层记忆

背景:重扫模型库后导航回根目录,打断连续操作

过程:恢复用户之前的文件夹导航深度

相关代码:[library-core.ts](file:///C:/Users/zhujieling11/frontend/src/menus/library-core.ts)


想象你在一座图书馆里。

你从一楼大厅进去,上了二楼,穿过走廊,找到科幻区,又往里走了两排书架——终于找到了你想要的那本书。

这时候,图书管理员说:"等一下,我要重新整理一下书架。" 然后管理员把整个图书馆重新摆了一遍。 摆完之后——你发现自己回到了一楼大厅。

"我刚在三楼科幻区呢!"你说。 "啊,抱歉,"管理员说,"重新整理之后,所有人都回到一楼。你再走上去吧。"

你气不气?


刷新的代价

"refreshLibrary 就是这样,"外交官说,"用户在模型库里翻了三层文件夹,找到了一个模型。这时候他点了一下'刷新'——或者扫描完新模型自动刷新——刷完之后,回到根目录了。"

"用户又得重新一层层点进去?"

"重新一层层点进去,"外交官点头,"一次两次还行,次数多了,用户会骂人的。"

"那为什么刷新之后会回到根目录?"

"因为简单啊,"外交官耸耸肩,"刷新 = 重新构建整个库。重新构建 = 从根目录开始。以前的代码就是这么写的——最简单,最直接,但是最不考虑用户感受。"

"那怎么改?"

"记一下楼层,"外交官说,"刷新之前,记住用户现在在第几层、路径是什么。刷新完了,照着原来的路径,再走一遍。如果路径还在——就回到那一层。如果路径不在了——文件夹被删了——再回根目录。"

"就像电梯有楼层记忆,停电了再来电,还记得你要去哪层。"

"就是这个意思,"外交官笑了。


记什么

"具体要记什么?"AI 问。

"记当前的路径,"外交官翻开 library-core.ts,找到 buildLevel 函数相关的代码。

"模型库的导航是一个栈——每进一层文件夹,就往栈里压一个路径。每退一层,就弹一个。栈顶就是当前所在的位置。"

"所以刷新之前,把这个栈存下来?"

"把栈存下来,"外交官确认,"然后刷新完了,从根目录开始,按照栈里的路径一层一层往下走。能走多深走多深。"

他在纸上画了个例子:

刷新前: 根 / 初音未来 / 公式服 / 晴天版本
           ↑    ↑        ↑        ↑
           0    1        2        3

刷新后: 根 / 初音未来 / 公式服 / (晴天版本被删了)
           ↑    ↑        ↑
           0    1        2  ← 停在这里,不回根目录

"如果最深的那一层不在了,就停在最近的存在的那一层。比直接回根目录强。"

"容错的思路。"

"对,容错,"外交官点头,"用户的路径可能部分失效——比如某个子文件夹被删了,但父文件夹还在。能恢复多少恢复多少,不要一失效就全丢了。"


怎么恢复

"具体怎么恢复?"

"刷新完之后,从根目录开始,遍历保存的路径,"外交官说,"每一层检查一下——这个子文件夹还在不在?在,就进去。不在,就停在这一层。"

他大致写了个思路:

typescript
// 刷新前保存
const savedPath = currentNavPath.slice(); // 拷贝一份

// 刷新后恢复
let currentLevel = rootLevel;
for (const folderName of savedPath) {
    const nextLevel = currentLevel.children.find(c => c.name === folderName);
    if (!nextLevel) break; // 找不到了,停在这里
    currentLevel = nextLevel;
}
// 最后渲染 currentLevel

"简单吧?"

"简单,"AI 说,"那为什么之前没做?"

"因为没想到,"外交官说,"或者说,觉得'不就是多点几下吗,多大点事'。但用户体验就是这些'多大点事'堆出来的。"

"刷新一次多点三下,一天刷新十次,就是三十下。三十下不多,但每一下都在提醒用户——'这个软件有点笨'。"


按名字匹配 vs 按路径匹配

"等等,"AI 想到一个问题,"你是按文件夹名字匹配的?如果有两个同名文件夹呢?"

"好问题,"外交官说,"但在文件系统里,同一个父目录下不会有两个同名文件夹。所以按名字匹配是安全的——只要路径是对的,每一层的名字就是唯一的。"

"那如果是 zip 文件呢?zip 里面的目录结构也一样?"

"一样,"外交官确认,"zip 内的路径也是层级的,同级不重名。所以按名字匹配没问题。"

"那为什么不存完整路径字符串,直接用路径匹配?"

"也可以,"外交官说,"但遍历栈有一个好处——每一层都检查,中间哪一层没了就停在哪一层。用完整路径字符串的话,你得自己解析路径,还要处理斜杠、大小写什么的。用栈遍历更直接,也更容错。"

"各有优劣?"

"各有优劣,"外交官点头,"但这里栈遍历更简单,也更容错——中间失效了能停在最近的有效层。"


为什么这是中优

"我有个问题,"AI 说,"这个功能,说重要吧——也重要,用户体验确实好。说不重要吧——也没那么重要,不就是多点几下吗。为什么是中优?"

"好问题,"外交官说,"优先级怎么定?我有一个简单的判断标准。"

他伸出三根手指:

第一,用户会不会骂娘? "会骂娘的是高优。比如崩溃、数据丢失、安全漏洞。"

第二,用户会不会皱眉? "会皱眉的是中优。比如多点几下、偶尔卡一下、界面有点别扭。不会骂,但心里不舒服。"

第三,用户会不会注意到? "注意不到的是低优。比如代码风格、内部重构、性能微优化。做了更好,不做也没人说。"

"那这个导航深度恢复属于哪一类?"

"第二类——皱眉,"外交官说,"用户不会骂人,但每次刷新都要重新点进去,心里会想'怎么又回去了'。不舒服,但也不是不能用。"

"中优的意思就是——做了用户会觉得'嗯,这个软件挺贴心的',不做用户也能用,但用得没那么顺。"

"对,"外交官笑了,"中优项就是'贴心程度'。做的越多,用户越觉得这个软件懂他。"


第十颗石子

第十颗黄石子落进"已处理"的堆里。

这一颗,是关于"被记住"的感觉。

用户不喜欢每次都从零开始。 他来过的地方、他做过的选择、他翻到哪一页——如果软件能记住,用户就会觉得亲切。 如果记不住,用户就觉得这软件没心没肺。

"就像你常去的咖啡店,"外交官说,"店员记得你喝什么,你就觉得舒服。要是每次都要你重新说一遍,你就觉得——这家店怎么回事。"

"软件也是一样的,"他接着说,"记住用户的选择,是最便宜的贴心。"


附录:状态保持设计原则

原则说明
刷新不丢位置重新加载数据后,尽量恢复用户之前的导航位置、滚动位置、选中项
部分失效部分恢复路径部分失效时,停在最近的有效层,不要直接回起点
名字匹配优先同层级名字唯一的场景,按名字匹配比按索引匹配更可靠
成本极低,收益不低十几行代码,换用户一个'挺贴心'的印象。性价比极高
不要假设用户要重来永远不要假设'刷新了就该从头开始'。用户只是想刷新,不是想重启

教训:最便宜的用户体验优化,就是记住用户的选择。他翻到了哪一层、他选中了哪个、他滚到了哪里——刷新的时候不要丢。十几行代码的事,但用户会悄悄觉得——这个软件,挺懂我的。