一、使用Object.assign

Object.assign(this.panelObj, matchedItem);
  • 功能:将matchedItem的所有可枚举属性复制到this.panelObj对象中(浅拷贝)。如果属性已存在,则会被覆盖;如果不存在,则会被添加。

  • 响应式问题:在Vue 2中,如果matchedItem中有新属性(即this.panelObj原来没有的属性),那么这些新属性不会被Vue响应式系统追踪。这意味着,如果你在模板中使用了这些新属性,当它们改变时,视图不会更新。

  • 适用场景:当你确定this.panelObj已经具有matchedItem中的所有属性(即只是更新已有属性)时,可以使用。或者,在Vue 3中,由于使用了Proxy,可以直接使用Object.assign,因为Vue 3的响应式系统可以检测到新属性。

二、使用$set逐个属性设置

Object.keys(matchedItem).forEach(key => {
  this.$set(this.panelObj, key, matchedItem[key]);
});
  • 功能:遍历matchedItem的每个属性,并使用Vue的$set方法将其添加到this.panelObj中。$set方法会确保属性被添加为响应式属性,即当属性值变化时,视图会更新。

  • 响应式处理:无论this.panelObj原来是否具有该属性,使用$set都会确保该属性是响应式的。如果属性已存在,$set也会更新它,并触发响应式更新。

  • 适用场景:在Vue 2中,当需要向响应式对象添加新属性时,必须使用$set(或Vue.set)来确保新属性也是响应式的。

主要区别

  1. 响应式处理:Object.assign不会将新属性转换为响应式,而$set会。

  2. 性能:Object.assign是一次性操作,而$set是逐个属性设置,如果属性很多,可能会稍微慢一些。

  3. Vue版本:在Vue 2中,推荐使用$set来添加新属性;在Vue 3中,由于响应式系统基于Proxy,直接使用Object.assign也可以触发响应式更新。

示例说明

假设我们有一个Vue组件,其中panelObj是一个响应式对象,初始为空:

data() {
  return {
    panelObj: {}
  };
}

然后我们有一个matchedItem对象:

matchedItem = {
  name: 'Alice',
  age: 30
};

使用Object.assign

Object.assign(this.panelObj, matchedItem);

此时,this.panelObj会变成{ name: 'Alice', age: 30 }。但是,如果panelObj原本没有这些属性,那么在Vue 2中,nameage这两个属性不是响应式的。这意味着,如果你在模板中使用了{{ panelObj.name }},然后以后修改了this.panelObj.name,视图不会更新。

使用$set

Object.keys(matchedItem).forEach(key => {
  this.$set(this.panelObj, key, matchedItem[key]);
});

这样设置后,this.panelObj同样变成{ name: 'Alice', age: 30 },并且每个属性都是响应式的。以后修改this.panelObj.name,视图会更新。

三、Object.assign() 简介

Object.assign() 方法用于对象的合并,将所有可枚举属性的值从一个或多个源对象复制到目标对象。

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。

注意:returnedTarget时target的引用。

const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

另外,如果将数组做为源对象,目标对象为{},那么结果可能会出乎你的意料。

const menuIds = [3,17]
const me = Object.assign({}, menuIds) 
console.log(me) // {0: 3, 1: 17}

menuIds:

 

me:

注意点

(1)浅拷贝

Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);

obj1.a.b = 2;
obj2.a.b // 2

上面代码中,源对象obj1a属性的值是一个对象,Object.assign拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。

(2)同名属性的替换

对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。

const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }

上面代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a: { b: 'hello', d: 'e' } }的结果。这通常不是开发者想要的,需要特别小心。

项目案例:

从父页面传递过来的集合:

save(edit) {
        this.$refs['form'].validate((valid) => {
          if (valid) {
            this.dataList.forEach(item => {
              let data = Object.assign( edit, {
                supplyId: item.supplyId,
                prepareId: item.id
              });
              console.log(edit)
              console.log(data)
              send.saveSendForPrepare(data).then(response => {
                if (response.success) {
                  this.$message.success('恭喜你,保存成功');
                  this.returnMessage();
                  this.returnMessage1();
                } else {
                  this.$message.error(response.msg);
                }
              }).catch(error => {
                this.$message.error('抱歉,保存失败');
                console.log(error);
              }).finally(() => {
                this.dialogFormVisible = false;
              })
            })
          }
        });
      }

第一次遍历,edit的值为:

 data的值为:

 第二次遍历edit的值为:

 data的值为:

  由上面发现,data是edit的引用,两次遍历过程中,Object.assign合并后返回同一个对象edit,即第二次遍历后的edit覆盖了第一次遍历时的edit值,

 原因分析:

当执行以下代码:

let data = Object.assign( edit, {
                supplyId: item.supplyId,
                prepareId: item.id
});

由于data是edit的引用,所以当第一次遍历数组时,edit为{prepareId:708,supplyId:913},此时data的值与edit的值是一样的。当第二次遍历数组时,edit的两个属性prepareId和supplyId的值被覆盖,变为{prepareId:707,supplyId:913},Object.assign拷贝得到的是这个对象edit的引用,当第二次执行Object.assign时,第一次data的值也跟着发生了变化。

 解决方法:如果希望合并后返回一个新对象,而不是原来的对象edit,对一个空对象合并。


save(edit) {
this.$refs['form'].validate((valid) => {
if (valid) {
this.dataList.forEach(item => {
let data = Object.assign({}, edit, {
supplyId: item.supplyId,
prepareId: item.id
});
console.log(edit)
console.log(data)
send.saveSendForPrepare(data).then(response => {
if (response.success) {
this.$message.success('恭喜你,保存成功');
this.returnMessage();
this.returnMessage1();
} else {
this.$message.error(response.msg);
}
}).catch(error => {
this.$message.error('抱歉,保存失败');
console.log(error);
}).finally(() => {
this.dialogFormVisible = false;
})
})
}
});
}
 

第一次遍历,edit的值为:

 data的值为:

 第二次遍历edit的值为:

 data的值为:

 由上面发现,edit的值一直保持不变,Object.assign合并后返回一个新对象。

总结:在forEach循环中,当用Object.assign() 方法合并对象时,最好对一个空对象合并,如果合并到变量(对象)上,那么每次的循环时,合并后的对象都是一样的。

posted on 2026-01-23 16:07  周文豪  阅读(1)  评论(0)    收藏  举报