背景

用户点击后立即看到结果,体验很好;但失败回滚若恢复整份旧快照,可能覆盖期间已经成功的另一次修改。

核心判断

  1. 给操作分配版本或请求序号,响应只能提交到匹配的状态。
  2. 按业务项记录逆操作,避免整页数据回滚。
  3. 金额、库存等强约束操作由服务端结果裁决,界面明确 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 构造。乐观更新不等于后端幂等,也不能解决并发写冲突。

验证方式

示例将单次操作期间的后续操作串行化。用两次修改乱序成功、失败和超时验证回滚,不让一次旧失败清掉新结果。

小结

保存可回滚版本并给变更分配序号,失败时只撤销仍属于当前操作的部分。