让你的应用瞬时响应,使用 Optimistic Updates
I’m happy to translate the article for you, but I need the full text of the post. Could you please paste the content you’d like translated (excluding the source line you’ve already provided)? Once I have the article text, I’ll translate it into Simplified Chinese while preserving the original formatting, markdown, and code blocks.
那一瞬间的摩擦
你知道在点击 “赞” 时,心形图标填满前的那一点点等待吗?那一瞬间你不确定是否成功的感觉?这不仅让人烦恼——它在侵蚀用户与界面之间的信任。
我在构建 AI 聊天功能时深有体会。用户发送消息后,消息在聊天窗口出现前会有一段尴尬的停顿。虽然 API 大约在 200 ms 内返回,但感觉却很迟钝。人们会再次点击 发送,以为之前的点击没有生效。功能本身很快,却没有 感觉 快。
这时我发现了 乐观更新(optimistic updates),说实话,它彻底改变了我对构建界面的思考方式。
实际等待时发生了什么
大多数应用的流程是这样的:
- 用户执行某个操作。
- 显示加载指示。
- 等待服务器确认。
- 更新 UI。
这种方式安全且可预测,但也会让你的应用感觉像在泥浆中前行。
从用户的角度来看,他们只是让你的应用去做一件事。他们点击了按钮,做出了决定。为什么要等服务器的许可才能看到自己操作的结果?
关键是,大多数操作都会成功。约 99 % 的情况下 API 调用会返回成功,我们却让用户为那 1 % 的失败概率等待。
乐观的做法
思维转变: 假设操作会成功,立即更新 UI,只有在真的出错时再处理失败。
- 当有人在聊天中发送消息时,立刻显示出来。
- 在后台进行 API 调用。
- 如果调用失败(极少),显示一个小的 “发送失败” 提示,并让用户重试。
我用这种模式重写了 AI 聊天功能。点击 发送 时消息立即出现,AI 的打字动画也立刻开始。后台把消息保存到数据库,但用户无需等待确认就能感受到自己的操作已生效。
结果: 天壤之别。人们不再双击 发送。功能变得响应迅速且充满活力,而不是迟钝且脱节。
删除的故事
另一个让我深有体会的场景是一个简单的 待办事项列表 功能。常规流程:
- 点击 删除。
- 显示加载状态。
- 调用 API。
- 从列表中移除该项。
我改为使用乐观更新:
- 点击 删除 → 项目立即消失。
- API 调用在后台悄悄进行。
- 如果调用失败,项目重新出现并弹出提示:“删除失败,请重试。”
虽然实际的 API 响应时间没有变化,但感知性能却飞涨。用户感觉交互 很快。
大家常犯的错误
我最初做错的(也是很多实现中常见的)是 没有正确处理错误情况。
编写成功路径很容易:
// 乐观更新,触发 API,完成
但如果调用失败且你没有保存之前的状态,就无法回滚。此时 UI 正在对用户撒谎。
这个模式需要三件事:
- 在做任何更改前保存当前状态。
- 乐观地更新 UI。
- 如果 API 调用 失败,恢复之前的状态并通知用户。
示例(删除项目)
function deleteItem(itemId) {
// 步骤 1:保存当前状态
const previousItems = [...items];
// 步骤 2:立即更新 UI(乐观)
items = items.filter(item => item.id !== itemId);
// 步骤 3:与服务器同步
api.deleteItem(itemId)
.then(() => {
// 成功!无需其他操作
})
.catch(() => {
// 失败!恢复之前的状态
items = previousItems;
showToast('Failed to delete item. Please try again.');
});
}
这个模式的美妙之处在于它适用于 任何 框架——React、Vue、Angular、Svelte——因为概念相同:立即更新,随后同步,必要时回滚。
Source: …
当不该使用乐观更新
在把每一个操作都设为乐观之前,先考虑哪些情况下不适合。
| 操作类型 | 建议 |
|---|---|
| 金融交易(例如电汇) | 不要 乐观;等待服务器确认。 |
| 永久删除重要数据 | 先显示确认对话框,然后对实际删除采用乐观更新。 |
| 简单可逆操作(例如点赞、切换) | 非常适合 乐观更新。 |
**经验法则:**如果操作易于撤销且失败概率低,就可以采用乐观更新;如果操作后果严重或需要确保得到确认,则等待服务器响应。
为什么这比你想象的更重要
用户并不会有意识地注意到速度快的体验;他们会感受到慢的体验。那 300 ms 的服务器等待时间?用户可能测不出具体数字,但会有迟滞的感觉。
使用乐观更新的应用会显得 原生、响应迅速,好像界面在实时倾听你的操作,而不是每次呼吸都要向服务器请求许可。
- Instagram 不会让你等到点赞显示。
- Twitter 不会让你等到转发显示。
- Gmail 在归档邮件时也不会让你等待(但会提供“撤销”选项以防万一)。
这些产品都明白 感知性能 与原始延迟同等重要,甚至更重要。通过在合适的场景下采用乐观更新,你可以让你的应用瞬间“活”起来。
用户之所以觉得快,是因为他们相信你的操作会成功,且把失败视为例外而非常态。
实际实现
我最初使用这种模式时,以为需要重写大量代码。结果发现,可以逐步引入。先挑选一个感觉慢的功能,给它加上乐观更新,感受一下效果。
从低风险的 点赞 按钮或简单的切换开关开始。熟悉 store → update → sync → rollback 的流程。做几次后,这套思路就会变成第二天性。
代码并不复杂,关键是思维方式的转变。你从“先请求许可,再执行”转变为“先执行,再同步”。
小结
乐观更新并不是只属于拥有庞大工程团队的大公司的一种高级技巧。它是一种简单的模式,却能显著提升应用的使用感受。
下次构建功能时,如果你发现自己要加加载指示器,问问自己:用户真的需要等吗?能否立刻展示结果并在后台同步?
大多数情况下,答案是 是。用户会因此感激你,即使他们并未明确意识到为什么你的应用使用起来更顺手。
如果你觉得本文对你有帮助,我会在 LinkedIn 上分享更多实用的前端技巧和现代框架开发的真实经验。欢迎联系并分享你在乐观更新方面的实践体会。