背景
用户点击后立即看到结果,体验很好;但失败回滚若恢复整份旧快照,可能覆盖期间已经成功的另一次修改。
核心判断
- 给操作分配版本或请求序号,响应只能提交到匹配的状态。
- 按业务项记录逆操作,避免整页数据回滚。
- 金额、库存等强约束操作由服务端结果裁决,界面明确 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 构造。乐观更新不等于后端幂等,也不能解决并发写冲突。
验证方式
示例将单次操作期间的后续操作串行化。用两次修改乱序成功、失败和超时验证回滚,不让一次旧失败清掉新结果。
小结
保存可回滚版本并给变更分配序号,失败时只撤销仍属于当前操作的部分。