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识别处理 |
关键点:
-
两者都是浅拷贝:对于嵌套对象/数组,只复制引用
-
在Vue3中都需要注意:如果希望嵌套对象也是响应式的,需要使用
reactive()包裹或使用ref() -
建议使用深拷贝的场景:需要完全独立的数据副本时
javascript
// 如果需要深拷贝,可以使用:
import { cloneDeep } from 'lodash-es';
// 或
JSON.parse(JSON.stringify(sourceData));
// 或
structuredClone(sourceData); // 现代浏览器原生方法

6631

被折叠的 条评论
为什么被折叠?



