Vue3中Object.assign用法区别解析

vue3中,
Object.assign(localCapitalInfo, sourceData);
与
Object.assign(localCapitalInfo, { ...sourceData });
有什么区别?

在 Vue3 中,这两种写法有 重要的区别,主要体现在 响应式系统 的处理上:

主要区别

1. Object.assign(localCapitalInfo, sourceData)

javascript

// 直接合并 sourceData 到响应式对象
Object.assign(localCapitalInfo, sourceData);
  • 直接引用localCapitalInfo 的属性直接引用 sourceData 的属性

  • 可能失去响应性:如果 sourceData 中的属性值是普通对象(非响应式对象),这些属性在合并后 不会自动变成响应式

  • 修改源数据可能不会触发更新:后续修改 sourceData 不会影响 localCapitalInfo

2. Object.assign(localCapitalInfo, { ...sourceData })

javascript

// 先创建新对象再合并
Object.assign(localCapitalInfo, { ...sourceData });
  • 创建新对象{ ...sourceData } 创建了一个全新的对象

  • 可能保持响应性:在 Vue3 中,当新对象被合并到响应式对象时,Vue 会更好地处理响应式转换

  • 更安全:与源数据 sourceData 解耦

具体示例对比

vue

<script setup>
import { reactive, watch } from 'vue';

// 源数据
const sourceData = {
  name: '张三',
  address: { city: '北京', district: '朝阳区' }
};

// 响应式对象
const localCapitalInfo = reactive({
  id: 1,
  company: 'ABC公司'
});

// 方法1:直接合并
const updateMethod1 = () => {
  Object.assign(localCapitalInfo, sourceData);
  // 此时 localCapitalInfo.address 直接引用 sourceData.address
  // 如果 sourceData.address 不是响应式的,那么修改 address.city 不会触发更新
};

// 方法2:通过扩展运算符合并
const updateMethod2 = () => {
  Object.assign(localCapitalInfo, { ...sourceData });
  // 此时 localCapitalInfo.address 是新创建的对象
  // Vue 会将其转换为响应式(如果是 reactive 对象)
};

// 监听变化
watch(
  () => localCapitalInfo,
  (newVal) => {
    console.log('数据变化了:', newVal);
  },
  { deep: true }
);
</script>

Vue3 中的最佳实践

推荐方式:使用 reactive 或 ref

javascript

// 如果是 reactive 创建的响应式对象
import { reactive } from 'vue';

const localCapitalInfo = reactive({});
const sourceData = { name: 'test', value: 123 };

// 推荐:直接赋值给响应式对象的属性
Object.assign(localCapitalInfo, sourceData);
// 或
Object.assign(localCapitalInfo, { ...sourceData });

// 或者使用响应式API
const state = reactive({
  ...localCapitalInfo,
  ...sourceData
});

更安全的做法:确保响应式

javascript

import { reactive, toRefs } from 'vue';

// 如果 sourceData 可能来自非响应式源
const updateData = (sourceData) => {
  // 方法1:使用扩展运算符创建新对象
  Object.assign(localCapitalInfo, { ...sourceData });
  
  // 方法2:如果需要深度响应式,使用 reactive 包裹
  Object.assign(localCapitalInfo, reactive({ ...sourceData }));
  
  // 方法3:逐个属性赋值(最安全)
  Object.keys(sourceData).forEach(key => {
    localCapitalInfo[key] = sourceData[key];
  });
};

总结

特性Object.assign(target, source)Object.assign(target, { ...source })
引用关系直接引用源对象创建新对象,与源对象解耦
响应式处理可能失去响应性更容易被 Vue 转换为响应式
内存使用共享内存,更节省创建新对象,占用额外内存
安全性修改源数据可能影响目标与源数据隔离,更安全
推荐场景确定源数据是响应式对象不确定源数据的响应式状态

在 Vue3 中,推荐使用 Object.assign(localCapitalInfo, { ...sourceData }),因为它更安全,能确保数据与源解耦,并且 Vue 能更好地处理响应式转换。


两者都属于浅拷贝吗?

是的,两者都是浅拷贝(shallow copy),但具体行为有所不同。

两者都是浅拷贝

1. Object.assign(localCapitalInfo, sourceData)

javascript

const sourceData = {
  name: '张三',
  address: { city: '北京' },  // 对象类型
  tags: ['VIP', '会员']        // 数组类型
};

// 执行浅拷贝
Object.assign(localCapitalInfo, sourceData);

// 结果是:
console.log(localCapitalInfo.address === sourceData.address); // true,引用相同
console.log(localCapitalInfo.tags === sourceData.tags);       // true,引用相同

2. Object.assign(localCapitalInfo, { ...sourceData })

javascript

// 扩展运算符本身也是浅拷贝
Object.assign(localCapitalInfo, { ...sourceData });

// 结果是:
console.log(localCapitalInfo.address === sourceData.address); // true,引用相同
console.log(localCapitalInfo.tags === sourceData.tags);       // true,引用相同

浅拷贝的对比表格

特点两种方式共同的浅拷贝行为
第一层属性创建新的副本(值类型)
嵌套对象复制引用,共享内存
数组/对象属性保持相同引用
修改嵌套对象会影响源数据(如果未解耦)

细微差异演示

虽然都是浅拷贝,但在某些场景下有细微差异:

javascript

// 源数据
const sourceData = {
  a: { value: 1 },
  b: { value: 2 }
};

// 方式1:直接合并
const obj1 = {};
Object.assign(obj1, sourceData);
console.log(obj1 === sourceData);            // false
console.log(obj1.a === sourceData.a);        // true(浅拷贝)

// 方式2:先扩展再合并
const obj2 = {};
Object.assign(obj2, { ...sourceData });
console.log(obj2 === sourceData);            // false
console.log(obj2.a === sourceData.a);        // true(浅拷贝)

// 扩展运算符创建了中间对象,所以:
console.log(obj2 === { ...sourceData });     // false(每次扩展都创建新对象)

在 Vue3 中的实际影响

vue

<script setup>
import { reactive } from 'vue';

const sourceData = {
  basic: { name: '张三' },  // 嵌套对象
  scores: [90, 85, 88]     // 数组
};

const state = reactive({});

// 两种方式都是浅拷贝,嵌套对象引用相同
Object.assign(state, sourceData);
// 或
Object.assign(state, { ...sourceData });

// 修改嵌套对象会影响源数据
state.basic.name = '李四';
console.log(sourceData.basic.name); // '李四',源数据也被修改

// 要避免这种情况,需要深拷贝
Object.assign(state, JSON.parse(JSON.stringify(sourceData)));
</script>

总结

特征两种方式的共性细微区别
拷贝深度都是浅拷贝(第一层)无区别
嵌套对象处理都复制引用无区别
内存引用嵌套对象共享内存无区别
中间对象-方式2创建了中间对象
Vue响应式处理都需要Vue转换嵌套对象为响应式方式2更易被Vue识别处理

关键点:

  1. 两者都是浅拷贝:对于嵌套对象/数组,只复制引用

  2. 在Vue3中都需要注意:如果希望嵌套对象也是响应式的,需要使用 reactive() 包裹或使用 ref()

  3. 建议使用深拷贝的场景:需要完全独立的数据副本时

javascript

// 如果需要深拷贝,可以使用:
import { cloneDeep } from 'lodash-es';
// 或
JSON.parse(JSON.stringify(sourceData));
// 或
structuredClone(sourceData); // 现代浏览器原生方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值