Vue3 乐观更新:先改变界面,也要准备撤销
背景 用户点击后立即看到结果,体验很好;但失败回滚若恢复整份旧快照,可能覆盖期间已经成功的另一次修改。 核心判断 给操作分配版本或请求序号,响应只能提交到匹配的状态。 按业务项记录逆操作,避免整页数据回滚。 金额、库存等强约束操作由服务端结果裁决,界面明确 pending 状态。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 // DTO 列表只有原始字段;本例将更新串行化,防止旧回滚覆盖新操作。 async function update(action: Action) { if (pending.value) return; const before = items.value.map(item => ({ ...item })); pending.value = true; items.value = applyLocal(items.value, action); try { items.value = await api.update(action); } catch (error) { items.value = before; showError(error); } finally { pending.value = false; } } 边界与误区 structuredClone 不适用于所有 Vue 响应式对象;快照应从可克隆的原始 DTO 构造。乐观更新不等于后端幂等,也不能解决并发写冲突。 验证方式 示例将单次操作期间的后续操作串行化。用两次修改乱序成功、失败和超时验证回滚,不让一次旧失败清掉新结果。 小结 保存可回滚版本并给变更分配序号,失败时只撤销仍属于当前操作的部分。