Optimistic UI:先更新界面,再等服务器
点赞、收藏、勾选待办这类操作,失败率极低。既然几乎总会成功,为什么要让用户盯着 spinner 等服务器?乐观更新 (Optimistic UI) 的策略是:点击瞬间就把界面更新到「成功后的样子」,把真正的请求挪到后台;只有在真的失败时才回滚并提示。下面同一个点赞按钮,在「乐观」与「悲观」两种策略下、同样的网络延迟里,体感完全不同。
乐观策略下,UI 在 t+0 就更新了,网络延迟被完全藏在背后;只有命中那次注定失败的请求,才会在响应到达时回滚——这正是它的代价:偶发的「跳一下」。悲观策略永远正确,但每次操作都要付出全额延迟,且按钮在等待期
disabled。两者的取舍由失败的后果决定:点赞错了无伤大雅 → 乐观;转账金额 → 必须悲观。
乐观更新的骨架——先改 UI,失败再回滚:
async function like() {
const prev = snapshot(state);
applyChange(state); // 1. 立即更新 UI
render();
try {
await api.toggleLike(); // 2. 后台发请求
} catch (err) {
restore(state, prev); // 3. 失败回滚
render();
toast('网络错误,已撤销'); // 4. 失败信号
}
}
乐观更新的三个必备零件,缺一不可:其一,操作前快照旧状态 (prev);其二,失败时精确回滚到快照(并处理回滚期间用户又点了的竞态);其三,给用户一个失败信号——通常就是一条
Snackbar。若服务器返回的「真实值」和乐观猜测不同(如点赞数被别人同时改了),还要用响应数据校正,而不是简单确认。
它和 Yellow Fade 是互补的:乐观更新负责让变化来得快,Yellow Fade 负责让变化被看见。许多数据库 / 框架已内建这一模式(React 的 useOptimistic、SWR / React Query 的 optimistic
mutation),但理解上面三个零件,才知道它们在替你做什么。