Appearance
第 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 说,"这个功能,说重要吧——也重要,用户体验确实好。说不重要吧——也没那么重要,不就是多点几下吗。为什么是中优?"
"好问题,"外交官说,"优先级怎么定?我有一个简单的判断标准。"
他伸出三根手指:
第一,用户会不会骂娘? "会骂娘的是高优。比如崩溃、数据丢失、安全漏洞。"
第二,用户会不会皱眉? "会皱眉的是中优。比如多点几下、偶尔卡一下、界面有点别扭。不会骂,但心里不舒服。"
第三,用户会不会注意到? "注意不到的是低优。比如代码风格、内部重构、性能微优化。做了更好,不做也没人说。"
"那这个导航深度恢复属于哪一类?"
"第二类——皱眉,"外交官说,"用户不会骂人,但每次刷新都要重新点进去,心里会想'怎么又回去了'。不舒服,但也不是不能用。"
"中优的意思就是——做了用户会觉得'嗯,这个软件挺贴心的',不做用户也能用,但用得没那么顺。"
"对,"外交官笑了,"中优项就是'贴心程度'。做的越多,用户越觉得这个软件懂他。"
第十颗石子
第十颗黄石子落进"已处理"的堆里。
这一颗,是关于"被记住"的感觉。
用户不喜欢每次都从零开始。 他来过的地方、他做过的选择、他翻到哪一页——如果软件能记住,用户就会觉得亲切。 如果记不住,用户就觉得这软件没心没肺。
"就像你常去的咖啡店,"外交官说,"店员记得你喝什么,你就觉得舒服。要是每次都要你重新说一遍,你就觉得——这家店怎么回事。"
"软件也是一样的,"他接着说,"记住用户的选择,是最便宜的贴心。"
附录:状态保持设计原则
| 原则 | 说明 |
|---|---|
| 刷新不丢位置 | 重新加载数据后,尽量恢复用户之前的导航位置、滚动位置、选中项 |
| 部分失效部分恢复 | 路径部分失效时,停在最近的有效层,不要直接回起点 |
| 名字匹配优先 | 同层级名字唯一的场景,按名字匹配比按索引匹配更可靠 |
| 成本极低,收益不低 | 十几行代码,换用户一个'挺贴心'的印象。性价比极高 |
| 不要假设用户要重来 | 永远不要假设'刷新了就该从头开始'。用户只是想刷新,不是想重启 |
教训:最便宜的用户体验优化,就是记住用户的选择。他翻到了哪一层、他选中了哪个、他滚到了哪里——刷新的时候不要丢。十几行代码的事,但用户会悄悄觉得——这个软件,挺懂我的。