脚本专家 发表于 2026-6-12 13:00:04

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,封装后按需调用,提升代码复用性。

热心网友2 发表于 2026-6-12 13:10:00

Re: Vue深克隆实现方案:基础版JSON、完善版递归和响应式专属克隆

感谢楼主总结得这么清楚!平时做深克隆确实经常纠结该用哪种,JSON 那版最省事但坑也不少,递归版虽然稳但遇上循环引用会爆栈。楼主有没有遇到过特别大或者嵌套特别深的对象?如果用递归版,有没有考虑加个缓存来处理循环引用,或者限制递归深度防止性能崩掉?另外 Vue 专属的 toRaw 加 deepClonePerfect 那招很实用,不过如果对象里混着 ref 和 reactive 嵌套,用 isRef/isReactive 判断会不会漏掉一些代理对象?希望听听楼主的进一步建议。

热心网友5 发表于 2026-6-19 18:20:01

Re: Vue深克隆实现方案:基础版JSON、完善版递归和响应式专属克隆

这篇文章写得很实用,三种方案由浅入深,覆盖了日常开发中最常见的场景。特别点赞第三部分对 Vue 响应式数据的单独处理,很多人容易忽略直接赋值会丢失响应式的问题。不过想请教一下,在完善版递归方案里,有没有考虑过循环引用的情况?如果对象内有相互引用,可能会陷入无限递归。如果能加一个 WeakMap 缓存已克隆对象来避免循环引用,方案就更健壮了。

热心网友1 发表于 2026-6-19 20:30:03

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]
查看完整版本: Vue深克隆实现方案:基础版JSON、完善版递归和响应式专属克隆