You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue+Vuex应用CRUD操作中,如何追踪需更新的数据?

确定Vuex应用中需API Patch的变更内容方案

思路3:响应式快照对比(推荐)

不用绑定一堆变更事件,也能精准识别真正的脏数据:

  • 从API拉取数据后,给每个实体对象加一个不可枚举的_original属性,存一份该对象的深拷贝快照(嵌套实体也得拷贝)
  • 借助Vue双向绑定自动更新数据,用户编辑时不用额外处理标记
  • 保存时遍历数据,对比每个对象和它的_original快照:
    • 只有当实际值和快照不一样时,才标记为需要Patch的对象
    • 新增的对象直接标记为_isNew,保存时走Post接口
    • 就算用户编辑后又改回原值,对比后会自动排除,不会误标

简单代码示例:

// 拉取数据时添加快照
async fetchData() {
  const res = await api.get('/entities');
  this.entities = res.data.map(item => {
    const original = JSON.parse(JSON.stringify(item));
    // 加不可枚举属性,不污染数据结构
    Object.defineProperty(item, '_original', {
      value: original,
      enumerable: false,
      writable: false
    });
    return item;
  });
}

// 保存时对比生成请求
async saveChanges() {
  const patchTasks = [];
  const postTasks = [];

  this.entities.forEach(item => {
    if (item._isNew) {
      postTasks.push(api.post('/entities', item));
    } else {
      // 对比当前值和快照,生成差异对象
      const diff = getDiff(item, item._original);
      if (Object.keys(diff).length > 0) {
        patchTasks.push(api.patch(`/entities/${item.id}`, diff));
      }
    }
  });

  await Promise.all([...patchTasks, ...postTasks]);
  
  // 保存后更新快照,把当前值设为新的原始值
  this.entities.forEach(item => {
    if (!item._isNew) {
      Object.defineProperty(item, '_original', {
        value: JSON.parse(JSON.stringify(item)),
        enumerable: false,
        writable: false
      });
    } else {
      delete item._isNew;
    }
  });
}

// 简易差异生成函数(也可以用工具库)
function getDiff(current, original) {
  const diff = {};
  for (const key in current) {
    if (typeof current[key] === 'object' && current[key] !== null) {
      const nestedDiff = getDiff(current[key], original[key]);
      if (Object.keys(nestedDiff).length > 0) {
        diff[key] = nestedDiff;
      }
    } else if (!Object.is(current[key], original[key])) {
      diff[key] = current[key];
    }
  }
  return diff;
}

思路2的工具选择(不用自己写深拷贝/对比)

如果一定要用深拷贝+全量对比,直接用现成工具:

  • 深拷贝:用lodash.cloneDeep,能处理嵌套实体、循环引用这些边缘情况
  • 深对比:lodash.isEqual可以精准对比对象所有层级的内容
  • 生成差异:用deep-object-diff库直接生成Patch需要的差异结构,不用自己写对比逻辑

示例代码:

import { cloneDeep, isEqual } from 'lodash';
import { diff } from 'deep-object-diff';

// 拉取数据时存两份:原始数据和可编辑数据
async fetchData() {
  const rawData = await api.get('/entities');
  this.rawEntities = cloneDeep(rawData);
  this.entities = cloneDeep(rawData);
}

// 保存时处理变更
async saveChanges() {
  const patchTasks = [];
  const postTasks = [];

  this.entities.forEach(item => {
    if (!item.id) { // 假设新增数据没有id
      postTasks.push(api.post('/entities', item));
    } else {
      const original = this.rawEntities.find(e => e.id === item.id);
      const changes = diff(original, item);
      if (Object.keys(changes).length > 0) {
        patchTasks.push(api.patch(`/entities/${item.id}`, changes));
      }
    }
  });

  await Promise.all([...patchTasks, ...postTasks]);
  // 更新原始数据
  this.rawEntities = cloneDeep(this.entities);
}

额外优化点

  • 分页数据不用全量对比,只对比当前页和标记过修改的页,提升性能
  • 嵌套实体如果是独立资源,可以单独处理其变更,比如只Patch嵌套实体的特定属性,减少请求数据量
  • 弹窗新增数据时,给对象加_isNew标记,方便区分Post/Patch操作

内容的提问来源于stack exchange,提问作者goodson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 22:15:37