Vue深克隆实现方案:基础版JSON、完善版递归和响应式专属克隆
在Vue开发中,深克隆用于复制响应式数据,确保修改副本不影响原对象,同时不丢失响应式特性。本文从浅克隆与深克隆的区别入手,介绍三种实用方案,覆盖简单数据、复杂数据类型和Vue响应式对象。一、浅克隆与深克隆的核心区别
浅克隆只复制对象的第一层属性,嵌套对象仍共享引用。例如Object.assign()和扩展运算符(...)均为浅克隆。深克隆递归复制所有层级,副本与原对象完全独立。在Vue中,直接克隆reactive或ref对象会丢失响应式,需要特殊处理。
二、方案1:基础版深克隆(JSON序列化)
适用于不包含函数、Date、RegExp、undefined等复杂类型的普通对象和数组。利用JSON.stringify()序列化再JSON.parse()反序列化,代码极简。
function deepCloneBasic(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
return JSON.parse(JSON.stringify(obj));
}
测试示例:
const obj = { name: 'Vue', info: { version: '3.4.21' }, list: };
const cloneObj = deepCloneBasic(obj);
cloneObj.info.version = '3.5.0';
cloneObj.list.push(4);
console.log(obj.info.version); // 3.4.21
console.log(obj.list); //
优点:简单直接;缺点:无法处理函数、Date、RegExp、Symbol等类型,会丢失或变形。
三、方案2:完善版深克隆(递归处理所有数据类型)
比基础版更通用,递归遍历对象,对Date、RegExp等特殊类型分别创建新实例,并区分数组和普通对象。
function deepClonePerfect(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj.source, obj.flags);
}
const cloneObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj = deepClonePerfect(obj);
}
}
return cloneObj;
}
测试复杂数据:
const complexObj = {
name: '深克隆',
date: new Date(),
reg: /vue/g,
func: () => console.log('vue'),
info: { a: 1, b: { c: 2 } },
list: ]
};
const cloneComplex = deepClonePerfect(complexObj);
cloneComplex.info.b.c = 100;
cloneComplex.list.push(4);
cloneComplex.date.setFullYear(2025);
console.log(complexObj.info.b.c); // 2
console.log(complexObj.list); //
console.log(complexObj.date.getFullYear()); // 2024
优点:克隆彻底,所有类型无误;缺点:代码稍长,但无外部依赖。
四、方案3:Vue专属深克隆(保留响应式)
针对Vue2和Vue3的响应式数据(ref、reactive、observable),克隆后重新创建响应式对象,避免丢失响应式特性。
Vue3 专属实现:
import { ref, reactive, isRef, isReactive, toRaw } from 'vue'
function deepCloneVue3(obj) {
if (isRef(obj)) {
const rawValue = toRaw(obj.value);
const cloneValue = deepClonePerfect(rawValue);
return ref(cloneValue);
}
if (isReactive(obj)) {
const rawObj = toRaw(obj);
const cloneObj = deepClonePerfect(rawObj);
return reactive(cloneObj);
}
return deepClonePerfect(obj);
}
测试:
const reactiveObj = reactive({ name: 'Vue3', info: { age: 5 } });
const refObj = ref({ a: 1, b: });
const cloneReactive = deepCloneVue3(reactiveObj);
const cloneRef = deepCloneVue3(refObj);
cloneReactive.info.age = 10;
cloneRef.value.b.push(4);
console.log(reactiveObj.info.age); // 5
console.log(refObj.value.b); //
Vue2 专属实现:
import Vue from 'vue'
function deepCloneVue2(obj) {
if (Vue.isReactive(obj) || Vue.isVue(obj)) {
const TempComponent = Vue.extend({
props: Object.keys(obj),
render: function() {}
});
const instance = new TempComponent({ propsData: obj });
return instance._props;
}
if (obj && obj._isRef) {
const cloneValue = deepClonePerfect(obj.value);
return Vue.ref(cloneValue);
}
return deepClonePerfect(obj);
}
测试:
const vue2Reactive = Vue.observable({ name: 'Vue2', list: });
const cloneVue2 = deepCloneVue2(vue2Reactive);
cloneVue2.list.push(3);
console.log(vue2Reactive.list); //
console.log(cloneVue2.list); //
优点:克隆后的数据仍具备响应式;缺点:依赖Vue版本,需区分Vue2和Vue3写法。
五、注意事项
- 不要用JSON方案克隆响应式数据,会丢失响应式。
- Vue3中克隆reactive对象前需用toRaw()转为原始对象,避免代理干扰。
- 避免克隆Vue组件实例、Vuex状态等特殊对象,可能引发异常。
- 频繁使用时建议封装为全局工具函数(如utils/clone.js)。
- 简单场景用方案1,复杂非响应式数据用方案2,响应式数据用方案3。
六、总结与选择建议
- 普通对象/数组无复杂类型:方案1(JSON序列化),简洁高效。
- 所有数据类型(非响应式):方案2(递归完善版),克隆彻底。
- Vue响应式数据(ref/reactive/observable):方案3(Vue专属版),保留响应式。
实际开发中可组合方案2和方案3,封装后按需调用,提升代码复用性。
Re: Vue深克隆实现方案:基础版JSON、完善版递归和响应式专属克隆
感谢楼主总结得这么清楚!平时做深克隆确实经常纠结该用哪种,JSON 那版最省事但坑也不少,递归版虽然稳但遇上循环引用会爆栈。楼主有没有遇到过特别大或者嵌套特别深的对象?如果用递归版,有没有考虑加个缓存来处理循环引用,或者限制递归深度防止性能崩掉?另外 Vue 专属的 toRaw 加 deepClonePerfect 那招很实用,不过如果对象里混着 ref 和 reactive 嵌套,用 isRef/isReactive 判断会不会漏掉一些代理对象?希望听听楼主的进一步建议。Re: Vue深克隆实现方案:基础版JSON、完善版递归和响应式专属克隆
这篇文章写得很实用,三种方案由浅入深,覆盖了日常开发中最常见的场景。特别点赞第三部分对 Vue 响应式数据的单独处理,很多人容易忽略直接赋值会丢失响应式的问题。不过想请教一下,在完善版递归方案里,有没有考虑过循环引用的情况?如果对象内有相互引用,可能会陷入无限递归。如果能加一个 WeakMap 缓存已克隆对象来避免循环引用,方案就更健壮了。Re: Vue深克隆实现方案:基础版JSON、完善版递归和响应式专属克隆
很实用的分享,三种方案覆盖了从简单到复杂再到Vue专属的场景,代码示例清晰,对开发中复制数据避免引用污染很有帮助。 我注意到方案2的递归实现里有个小笔误:`cloneObj = deepClonePerfect(obj);` 这里应该是 `deepClonePerfect(obj)` 吧?另外,如果对象存在循环引用,这个递归版本会陷入死循环,可以补充一个WeakMap来缓存已拷贝的对象进行防循环处理。 Vue2的专属克隆用了组件实例化,思路挺巧,但性能开销稍大,更轻量的方式可能是先用`JSON.parse(JSON.stringify(toRaw(obj)))`再`new Vue({data: clone})`或者直接用`Object.assign`配合`toRaw`。Vue3部分用`toRaw`+`deepClonePerfect`再重新包装成响应式对象,逻辑很清晰。 对于一般项目,如果不需要考虑Date、RegExp等,JSON方案最省心;如果有复杂类型或响应式需求,你的方案可以直接拿来用。感谢分享!
页:
[1]