Web 平台 API / ✦ UX 交互模式 · 那些有名字的交互细节 / Optimistic UI:先更新界面,再等服务器 待审核 1 / 7
optimistic · 乐观更新

Optimistic UI:先更新界面,再等服务器

点赞、收藏、勾选待办这类操作,失败率极低。既然几乎总会成功,就没必要让用户盯着 spinner 等服务器。乐观更新 (Optimistic UI) 的策略是:点击瞬间就把界面更新到成功后的样子,把真正的请求挪到后台;只有在真的失败时才回滚并提示。

图 0-1 · 同一个点赞按钮在乐观与悲观两种策略下的时间线对照。可拖动延迟滑块、打开失败开关,看回滚是怎么发生的。

乐观策略下,UI 在 t+0t+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),但理解上面三个零件,才知道它们在替开发者做什么。