回滚时机需要清晰地把界面带回失败前的状态而非留下过渡痕迹
别名: 干净回滚 · 过渡残留 · pending ghost · 失败前状态
概念解释
乐观写入失败,屏幕要回到按下之前那一帧的世界:未点亮的心、列表里没有那条评论、开关在原来的那边。回滚的时机是权威拒绝到达的那一拍,动作是把乐观层揭掉,而不是在乐观层上再贴一层「失败中」「发送中」让它停在半道。留下的半道叫过渡痕迹——一条灰掉的评论、一颗半亮的心、一个永远转圈的发送箭头。痕迹既不是成功也不是失败前,人无法在上面做下一拍。
这条谈回滚把界面安放在哪一个状态。必须开口告诉人失败了,是另一项义务;这里要的是安放本身干净。
机制
乐观更新在界面状态机上插入了一个额外态:已本地提交、未权威确认。成功把这个态升成已确认;失败必须把它消去,让状态机回到失败前的那一个合法态。消去若做成「给这个态打失败补丁」,状态机多出一个产品从未设计过的合法态——半条评论、半颗心。后续交互(再点、删除、引用)会落在这个非法组合上,错误从一次网络失败变成长期的界面幽灵。
时机也很窄。回滚若等动画播完、等页面切走再倒,人已经基于乐观态做了下一眼的计划。晚到的倒退会与那一计划撞车。权威拒绝到达的那一帧就应完成消去:乐观像素消失,失败前像素在场。告知可以叠在这帧上,但不能靠留下半道来充当告知。
边界
需要人处理的失败(填一个验证码、选另一个收件人)会在失败前状态之上打开一个新的、有名字的对话框,那不是痕迹,是新任务。痕迹是没有名字的半态。离线排队里的「待发送」是合法态,因为它可查询、可取消、承诺稍后兑现;回滚失败不是排队,不应借用待发送的外观。动画可以有几帧从乐观画回失败前,只要终帧是失败前、中间帧不被当成可交互的新对象。列表虚拟化若在回滚后把行高或滚动位置弄乱,也是痕迹的一种——内容对了,空间还停在乐观过的布局上。
怎么落地
- 为每个乐观字段保存失败前快照,拒绝到达时用快照替换,不要在乐观值上改样式了事。
- 回滚终态里不得残留「发送中」「同步中」、半透明行、停住的进度。
- 告知叠在干净的失败前界面上(toast、行内错误),不靠半道对象自己说话。
- 验证:乐观点赞再强制失败。终帧应是未点亮,而不是一颗灰心或转圈。发一条评论再失败:列表里那一行应不在,滚动位置应像从未插入过。录屏逐帧看,从乐观到失败前之间若停在某一帧超过半秒且该帧可被点击,就是痕迹。