Vue中深克隆的3种实现方案

 更新时间:2026年04月28日 08:30:35   作者:前端那点事  
本文介绍了三种Vue开发中常用的深克隆实现方法,分别适用于不同场景,方案一适用于普通对象和数组,方案二适用于所有数据类型,方案三专门适配Vue的响应式数据,每种方案都有其优点和缺点,开发时可根据具体需求选择合适的的方法,提高开发效率,需要的朋友可以参考下

深克隆(深拷贝)的核心是复制一个对象/数组,且拷贝后的对象与原对象完全独立,修改拷贝对象不会影响原对象(包括嵌套层级的属性/元素)。在Vue开发中,深克隆常用于处理响应式数据(如ref、reactive创建的数据),避免因浅拷贝导致的响应式异常,以下是3种实用实现方案,从简单到完善,覆盖不同场景。

一、核心前提:浅克隆 vs 深克隆(必懂区别)

先明确浅克隆与深克隆的差异,避免混淆:

  • 浅克隆:仅复制对象的第一层属性,嵌套的对象/数组仍指向原引用(修改嵌套内容会影响原对象),如Object.assign()、扩展运算符(...)。
  • 深克隆:复制对象的所有层级(包括嵌套的对象、数组、函数等),拷贝后与原对象完全脱离引用,互不影响(这是Vue开发中常用的方式)。

Vue中注意点:对于reactive创建的响应式对象,直接克隆会丢失响应式特性,需特殊处理;ref对象需先获取.value再克隆。

二、方案1:基础版深克隆(适配普通对象/数组,无复杂类型)

适用于:普通对象、数组,无函数、Date、RegExp等复杂类型,代码简洁,上手快。

核心逻辑:利用JSON.stringify()将对象转为字符串,再用JSON.parse()转回对象,实现深度拷贝(本质是序列化+反序列化)。

// 基础版深克隆(Vue中可直接使用,适配简单数据)
function deepCloneBasic(obj) {
  // 处理null和非对象/数组类型(直接返回,无需克隆)
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  // 利用JSON序列化+反序列化实现深克隆
  return JSON.parse(JSON.stringify(obj));
}

// Vue中测试示例(普通数据)
const obj = { name: 'Vue', info: { version: '3.4.21' }, list: [1, 2, 3] };
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); // [1,2,3](原对象不变)

优点:代码简洁,无需复杂逻辑,适配大部分简单场景;

缺点:无法克隆函数、Date、RegExp、undefined、Symbol等复杂类型(会丢失或变形),不适合包含这些类型的数据。

三、方案2:完善版深克隆(适配所有类型,通用推荐)

适用于:所有数据类型(对象、数组、函数、Date、RegExp等),解决基础版的缺陷,是Vue开发中最常用的通用方案。

核心逻辑:递归遍历对象/数组,对不同类型的数据分别处理,确保所有层级都被深度拷贝,不丢失类型和属性。

// 完善版深克隆(适配所有类型,Vue通用)
function deepClonePerfect(obj) {
  // 1. 处理null和非对象类型(直接返回)
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // 2. 处理Date类型(避免Date被转为普通对象)
  if (obj instanceof Date) {
    return new Date(obj);
  }

  // 3. 处理RegExp类型(保留正则的修饰符)
  if (obj instanceof RegExp) {
    return new RegExp(obj.source, obj.flags);
  }

  // 4. 处理数组和对象(区分数组和普通对象)
  // 创建一个与原对象类型一致的空容器(数组→数组,对象→对象)
  const cloneObj = Array.isArray(obj) ? [] : {};

  // 5. 递归遍历所有属性,深度拷贝
  for (let key in obj) {
    // 只拷贝对象自身的属性(排除原型链上的属性)
    if (obj.hasOwnProperty(key)) {
      // 递归克隆子属性(嵌套对象/数组)
      cloneObj[key] = deepClonePerfect(obj[key]);
    }
  }

  return cloneObj;
}

// Vue中测试示例(复杂数据)
const complexObj = {
  name: '深克隆',
  date: new Date(),
  reg: /vue/g,
  func: () => console.log('vue'),
  info: { a: 1, b: { c: 2 } },
  list: [1, [2, 3]]
};

const cloneComplex = deepClonePerfect(complexObj);
cloneComplex.info.b.c = 100;
cloneComplex.list[1].push(4);
cloneComplex.date.setFullYear(2025);

console.log(complexObj.info.b.c); // 2(原对象不变)
console.log(complexObj.list[1]); // [2,3](原对象不变)
console.log(complexObj.date.getFullYear()); // 2024(原对象不变)
console.log(cloneComplex.func()); // vue(函数正常克隆)

优点:适配所有数据类型,克隆彻底,无数据丢失,满足Vue中复杂数据的克隆需求;

缺点:代码稍复杂,无需额外依赖,可直接封装到Vue项目中复用。

四、方案3:Vue专属深克隆(适配响应式数据,推荐)

适用于:Vue2/Vue3的响应式数据(ref、reactive创建的数据),解决“克隆后丢失响应式”的问题,结合Vue内置API,更贴合Vue开发场景。

核心逻辑:利用Vue内置的reactive、ref API,克隆后重新创建响应式对象,确保克隆后的数据仍保持响应式特性。

1. Vue3 专属实现(适配ref、reactive)

<script setup>
import { ref, reactive, isRef, isReactive, toRaw } from 'vue'

// Vue3专属深克隆(保留响应式)
function deepCloneVue3(obj) {
  // 处理ref对象:先获取原始值,克隆后重新创建ref
  if (isRef(obj)) {
    const rawValue = toRaw(obj.value); // 转为原始值(避免响应式干扰)
    const cloneValue = deepClonePerfect(rawValue); // 用完善版克隆原始值
    return ref(cloneValue); // 重新创建ref,保留响应式
  }

  // 处理reactive对象:先获取原始值,克隆后重新创建reactive
  if (isReactive(obj)) {
    const rawObj = toRaw(obj); // 转为原始对象
    const cloneObj = deepClonePerfect(rawObj); // 深度克隆
    return reactive(cloneObj); // 重新创建reactive,保留响应式
  }

  // 非响应式数据,直接用完善版克隆
  return deepClonePerfect(obj);
}

// 测试示例
const reactiveObj = reactive({ name: 'Vue3', info: { age: 5 } });
const refObj = ref({ a: 1, b: [2, 3] });

// 克隆响应式数据
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); // [2,3](原对象不变)
console.log(cloneReactive.info.age); // 10(克隆对象响应式正常)
</script>

2. Vue2 专属实现(适配data、ref、reactive)

// Vue2专属深克隆(保留响应式,需引入Vue)
import Vue from 'vue'

function deepCloneVue2(obj) {
  // 处理Vue2的响应式对象(data中定义的对象、Vue.observable创建的对象)
  if (Vue.isReactive(obj) || Vue.isVue(obj)) {
    // 用Vue.extend创建临时组件,通过props传递数据,实现响应式克隆
    const TempComponent = Vue.extend({
      props: Object.keys(obj),
      render: function() {}
    });
    const instance = new TempComponent({ propsData: obj });
    return instance._props;
  }

  // 处理ref对象(Vue2的ref需通过.value访问)
  if (obj && obj._isRef) {
    const cloneValue = deepClonePerfect(obj.value);
    return Vue.ref(cloneValue);
  }

  // 非响应式数据,用完善版克隆
  return deepClonePerfect(obj);
}

// 测试示例(Vue2)
const vue2Reactive = Vue.observable({ name: 'Vue2', list: [1, 2] });
const cloneVue2 = deepCloneVue2(vue2Reactive);
cloneVue2.list.push(3);
console.log(vue2Reactive.list); // [1,2](原对象不变)
console.log(cloneVue2.list); // [1,2,3](克隆对象响应式正常)

优点:专门适配Vue响应式数据,克隆后不丢失响应式特性,贴合Vue开发场景;

缺点:依赖Vue内置API,需区分Vue2和Vue3版本。

五、Vue中深克隆的注意事项

  • 克隆响应式数据时,不可直接用基础版(JSON方式),会丢失响应式特性,需用Vue专属方案;
  • Vue3中,reactive对象克隆前需用toRaw()转为原始对象,否则会因响应式代理导致克隆异常;
  • 避免克隆Vue组件实例、Vuex状态等特殊对象,这类对象克隆后可能无法正常使用;
  • 若项目中频繁使用深克隆,可将完善版/Vue专属版封装为全局工具函数(如utils/clone.js),方便复用;
  • 简单场景(无复杂类型、非响应式数据)可直接用基础版,复杂场景优先用完善版,响应式数据用Vue专属版。

六、总结(方案选择建议)

  1. 简单场景(普通对象/数组,无复杂类型):方案1(基础版),简洁高效;
  2. 通用场景(所有数据类型,非响应式):方案2(完善版),克隆彻底,无数据丢失;
  3. Vue开发场景(响应式数据):方案3(Vue专属版),保留响应式,贴合Vue特性。

实际开发中,可根据数据类型和是否为响应式,选择对应的方案,建议将方案2和方案3封装为全局工具,提升开发效率。

以上就是Vue中深克隆的3种实现方案的详细内容,更多关于Vue深克隆实现方案的资料请关注脚本之家其它相关文章!

相关文章

  • Vue渲染函数详解

    Vue渲染函数详解

    下面小编就为大家带来一篇Vue渲染函数详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • Nuxt.js 静态资源和打包的操作

    Nuxt.js 静态资源和打包的操作

    这篇文章主要介绍了Nuxt.js 静态资源和打包的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Vue实现微信支付功能遇到的坑

    Vue实现微信支付功能遇到的坑

    这篇文章主要介绍了Vue实现微信支付功能遇到的坑,本文是小编记录整理下拉的,以便日后所需,需要的朋友可以参考下
    2019-06-06
  • 详解Vue3的响应式原理解析

    详解Vue3的响应式原理解析

    这篇文章主要为大家介绍了Vue3的响应式原理解析,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • 手把手教你创建vue3项目的最佳方式

    手把手教你创建vue3项目的最佳方式

    如今的Vue3已经势不可挡,当然搭建一个全新的Vue3项目也有了全新的方式,下面这篇文章主要给大家介绍了关于如何手把手教你创建vue3项目的最佳方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-11-11
  • SpringBoot实现全局和局部跨域的两种方式

    SpringBoot实现全局和局部跨域的两种方式

    本文主要介绍了SpringBoot实现全局和局部跨域的两种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • vue路由插件之vue-route

    vue路由插件之vue-route

    这篇文章主要介绍了vue路由插件之vue-route的相关知识,本文通过实例代码给大家介绍了vue router的使用,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06
  • Vue利用vue-baidu-map实现获取经纬度和搜索地址

    Vue利用vue-baidu-map实现获取经纬度和搜索地址

    在开发项目的时候,发现需要获取经纬度,由于这个项目是用vue写的,最后决定使用vue-baidu-map来快速获取经纬度,感兴趣的可以了解一下
    2022-09-09
  • Vue中使用v-print打印出现空白页问题及解决

    Vue中使用v-print打印出现空白页问题及解决

    这篇文章主要介绍了Vue中使用v-print打印出现空白页问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • vue-cli创建项目及项目结构解析

    vue-cli创建项目及项目结构解析

    上一篇我们安装了vue-cli,接下来我们就使用该脚手架进行创建项目,这篇文章主要介绍了vue-cli创建项目以及项目结构的相关资料,需要的朋友可以参考下面文章的具体内容
    2021-10-10

最新评论