查看: 143|回复: 1

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

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在Vue开发中,深克隆用于复制响应式数据,确保修改副本不影响原对象,同时不丢失响应式特性。本文从浅克隆与深克隆的区别入手,介绍三种实用方案,覆盖简单数据、复杂数据类型和Vue响应式对象。

一、浅克隆与深克隆的核心区别

浅克隆只复制对象的第一层属性,嵌套对象仍共享引用。例如Object.assign()和扩展运算符(...)均为浅克隆。深克隆递归复制所有层级,副本与原对象完全独立。在Vue中,直接克隆reactive或ref对象会丢失响应式,需要特殊处理。

二、方案1:基础版深克隆(JSON序列化)

适用于不包含函数、Date、RegExp、undefined等复杂类型的普通对象和数组。利用JSON.stringify()序列化再JSON.parse()反序列化,代码极简。
  1. function deepCloneBasic(obj) {
  2.   if (obj === null || typeof obj !== 'object') {
  3.     return obj;
  4.   }
  5.   return JSON.parse(JSON.stringify(obj));
  6. }
复制代码

测试示例:
  1. const obj = { name: 'Vue', info: { version: '3.4.21' }, list: [1, 2, 3] };
  2. const cloneObj = deepCloneBasic(obj);
  3. cloneObj.info.version = '3.5.0';
  4. cloneObj.list.push(4);
  5. console.log(obj.info.version); // 3.4.21
  6. console.log(obj.list); // [1,2,3]
复制代码

优点:简单直接;缺点:无法处理函数、Date、RegExp、Symbol等类型,会丢失或变形。

三、方案2:完善版深克隆(递归处理所有数据类型)

比基础版更通用,递归遍历对象,对Date、RegExp等特殊类型分别创建新实例,并区分数组和普通对象。
  1. function deepClonePerfect(obj) {
  2.   if (obj === null || typeof obj !== 'object') {
  3.     return obj;
  4.   }
  5.   if (obj instanceof Date) {
  6.     return new Date(obj);
  7.   }
  8.   if (obj instanceof RegExp) {
  9.     return new RegExp(obj.source, obj.flags);
  10.   }
  11.   const cloneObj = Array.isArray(obj) ? [] : {};
  12.   for (let key in obj) {
  13.     if (obj.hasOwnProperty(key)) {
  14.       cloneObj[key] = deepClonePerfect(obj[key]);
  15.     }
  16.   }
  17.   return cloneObj;
  18. }
复制代码

测试复杂数据:
  1. const complexObj = {
  2.   name: '深克隆',
  3.   date: new Date(),
  4.   reg: /vue/g,
  5.   func: () => console.log('vue'),
  6.   info: { a: 1, b: { c: 2 } },
  7.   list: [1, [2, 3]]
  8. };
  9. const cloneComplex = deepClonePerfect(complexObj);
  10. cloneComplex.info.b.c = 100;
  11. cloneComplex.list[1].push(4);
  12. cloneComplex.date.setFullYear(2025);
  13. console.log(complexObj.info.b.c); // 2
  14. console.log(complexObj.list[1]); // [2,3]
  15. console.log(complexObj.date.getFullYear()); // 2024
复制代码

优点:克隆彻底,所有类型无误;缺点:代码稍长,但无外部依赖。

四、方案3:Vue专属深克隆(保留响应式)

针对Vue2和Vue3的响应式数据(ref、reactive、observable),克隆后重新创建响应式对象,避免丢失响应式特性。

Vue3 专属实现:
  1. import { ref, reactive, isRef, isReactive, toRaw } from 'vue'
  2. function deepCloneVue3(obj) {
  3.   if (isRef(obj)) {
  4.     const rawValue = toRaw(obj.value);
  5.     const cloneValue = deepClonePerfect(rawValue);
  6.     return ref(cloneValue);
  7.   }
  8.   if (isReactive(obj)) {
  9.     const rawObj = toRaw(obj);
  10.     const cloneObj = deepClonePerfect(rawObj);
  11.     return reactive(cloneObj);
  12.   }
  13.   return deepClonePerfect(obj);
  14. }
复制代码

测试:
  1. const reactiveObj = reactive({ name: 'Vue3', info: { age: 5 } });
  2. const refObj = ref({ a: 1, b: [2, 3] });
  3. const cloneReactive = deepCloneVue3(reactiveObj);
  4. const cloneRef = deepCloneVue3(refObj);
  5. cloneReactive.info.age = 10;
  6. cloneRef.value.b.push(4);
  7. console.log(reactiveObj.info.age); // 5
  8. console.log(refObj.value.b); // [2,3]
复制代码

Vue2 专属实现:
  1. import Vue from 'vue'
  2. function deepCloneVue2(obj) {
  3.   if (Vue.isReactive(obj) || Vue.isVue(obj)) {
  4.     const TempComponent = Vue.extend({
  5.       props: Object.keys(obj),
  6.       render: function() {}
  7.     });
  8.     const instance = new TempComponent({ propsData: obj });
  9.     return instance._props;
  10.   }
  11.   if (obj && obj._isRef) {
  12.     const cloneValue = deepClonePerfect(obj.value);
  13.     return Vue.ref(cloneValue);
  14.   }
  15.   return deepClonePerfect(obj);
  16. }
复制代码

测试:
  1. const vue2Reactive = Vue.observable({ name: 'Vue2', list: [1, 2] });
  2. const cloneVue2 = deepCloneVue2(vue2Reactive);
  3. cloneVue2.list.push(3);
  4. console.log(vue2Reactive.list); // [1,2]
  5. console.log(cloneVue2.list); // [1,2,3]
复制代码

优点:克隆后的数据仍具备响应式;缺点:依赖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 小时前 | 显示全部楼层

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

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

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 15:10 , Processed in 0.027793 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部