Vue 双重v-for渲染表单,再复制表单编辑之深拷贝

news2024/12/22 19:32:44

文章目录

  • 前言
  • 问题背景
  • 实现拷贝表单
  • 如何实现深拷贝
    • Object.assign
    • JSON实现的深拷贝
    • 递归实现
    • 解决循环引用的递归实现
    • require('lodash').cloneDeep()


前言

在做复杂的动态表单,实现业务动态变动,比如有一条需要动态添加的el-form-item中包含了多个输入框,并实现表单验证,但在element-ui组件库中给出的表单校验中没有这样的格式,解决方法可参考文章:Element-UI 实现动态增加多个输入框并校验。

如果不想要固定格式的动态增加表单,且增加表单的类型不同,比如按钮开关、文本输入框、数字输入框,想要自由增加不同类型的表单并验证,可以参考文章:Element-UI 实现动态增加多个不同类型的输入框并校验(双重v-for表单验证)。

如果还想要还要复制多套表单且可编辑,可以参考本文。


问题背景

在复制表单之后,对表单进行修改,发现所有表单的值都同时改变,分析:表单没有进行深拷贝,而是引用的其它表单的值。

实现拷贝表单

表单内容:

在这里插入图片描述

1、第一步坑🤔

在复制表单按钮的方法添加代码:

this.form.content.push(this.form.content[index])

在这里插入图片描述

复制,编辑,啊这。。。多套表单内容同时改变

2、第二步坑😥

this.form.content.push([...this.form.content[index]])

还是一样,... 深拷贝最外一层,内层还是引用。

3、实现

解决方法有两种:

① 序列化,再反序列化

  • 序列化:把对象转换为字节序列的过程称为对象的序列化
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化
this.form.content.push(JSON.parse(JSON.stringify(this.form.content[index])))

② require(‘lodash’).cloneDeep()

this.form.content.push(require('lodash').cloneDeep(this.form.content[index]))

在这里插入图片描述

既然如此,那就不得不重新学习深拷贝了。

如何实现深拷贝

Object.assign

Object.assign默认是对对象进行深拷贝的,但是我们需要注意的是,它只对最外层的进行深拷贝,也就是当对象内嵌套有对象的时候,被嵌套的对象进行的还是浅拷贝;

function cloneDeepAssign(obj){
  return Object.assign({},obj)
}

温馨提示:数组拷贝方法当中,使用…、slice、concat等进行拷贝也是一样的效果,只深拷贝最外层

同时,我们知道Object.assign针对的是对象自身可枚举的属性,对于不可枚举的没有效果;
所以,当我们对于一个层次单一对象的时候,可以考虑这种方法,简单快捷。(不支持undefined拷贝)

JSON实现的深拷贝

这是我们最最最常提到的一种深拷贝的方式,一般大部分的深拷贝都可以用JSON的方式进行解决,本质是因为JSON会自己去构建新的内存来存放新对象。

先将需要拷贝的对象进行JSON字符串化,然后再pase解析出来,赋给另一个变量,实现深拷贝。

function cloneDeepJson(obj){
  return JSON.parse(JSON.stringify(obj))
}

这个方法有一些弊端,我们要注意的是:

  • 会忽略 undefined和symbol;
  • 不可以对Function进行拷贝,因为JSON格式字符串不支持Function,在序列化的时候会自动删除;
  • 诸如 Map, Set, RegExp, Date, ArrayBuffer 和其他内置类型在进行序列化时会丢失;
  • 不支持循环引用对象的拷贝;(循环引用的可以大概地理解为一个对象里面的某一个属性的值是它自己)

递归实现

function cloneDeepDi(obj){
  const newObj = {};
  let keys = Object.keys(obj);
  let key = null;
  let data = null;
  for(let i = 0; i<keys.length;i++){
    key = keys[i];
    data = obj[key];
    if(data && typeof data === 'object'){
      newObj[key] = cloneDeepDi(data)
    }else{
      newObj[key] = data;
    }
  }
  return newObj
}

虽然但是,它也是有缺点的,就是不能解决循环引用的问题,一旦出现了循环引用,就死循环了!

解决循环引用的递归实现

function deepCopy(obj, parent = null) {
  // 创建一个新对象
  let result = {};
  let keys = Object.keys(obj),
    key = null,
    temp = null,
    _parent = parent;
  // 该字段有父级则需要追溯该字段的父级
  while (_parent) {
    // 如果该字段引用了它的父级则为循环引用
    if (_parent.originalParent === obj) {
      // 循环引用直接返回同级的新对象
      return _parent.currentParent;
    }
    _parent = _parent.parent;
  }
  for (let i = 0; i < keys.length; i++) {
    key = keys[i];
    temp = obj[key];
    // 如果字段的值也是一个对象
    if (temp && typeof temp === 'object') {
      // 递归执行深拷贝 将同级的待拷贝对象与新对象传递给 parent 方便追溯循环引用
      result[key] = DeepCopy(temp, {
        originalParent: obj,
        currentParent: result,
        parent: parent
      });
    } else {
      result[key] = temp;
    }
  }
  return result;
}

大致的思路是:判断一个对象的字段是否引用了这个对象或这个对象的任意父级,如果引用了父级,那么就直接返回同级的新对象,反之,进行递归的那套流程。

但其实,还有一种情况是没有解决的,就是子对象间的互相引用,使用时需注意。

require(‘lodash’).cloneDeep()

var _ = require('lodash');
var obj1 = {
  a: 1,
  b: { f: { g: 1 } },
  c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f); // false

这是最最最最完美的深拷贝的方式,它已经将会出现问题的各种情况都考虑在内了,所以在日常项目开发当中,建议使用这种成熟的解决方案。

注:其实lodash解决循环引用的方式,就是用一个栈记录所有被拷贝的引用值,如果再次碰到同样的引用值的时候,不会再去拷贝一遍,而是利用之前已经拷贝好的。

进阶:Vue 复杂json数据在el-table表格中展示(el-table分割数据)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Tomcat服务器下载安装及配置教程(IDEA中使用Tomcat)

目录 友情提醒第一章、Tomcat下载与安装1.1&#xff09;Tomcat介绍1.2&#xff09;官网下载 第二章、Tomcat配置环境变量2.1&#xff09;windows环境变量配置2.2&#xff09;验证Tomcat配置是否成功2.3&#xff09;报错解决 第三章、IDEA整合Tomcat3.1&#xff09;打开IDEA开发…

【OAuth2】OAuth2概述及使用GitHub登录第三方网站

【OAuth2】OAuth2概述及使用GitHub登录第三方网站 文章目录 【OAuth2】OAuth2概述及使用GitHub登录第三方网站0. 导言1. OAuth2 简介2. OAuth2 认证授权总体流程3. OAuth2 标准接口4. OAuth2 四种授权模式4.1 授权码模式4.2 简化模式4.3 密码模式4.4 客户端模式 5. GitHub授权登…

2023河南萌新联赛第一场(河南农业大学)

目录 A.你也喜欢数学吗​编辑 E.动物朋友 F.松鼠排序 G.Reverse J.合唱比赛 K.以撒和隐藏房间 L.中位数 A.你也喜欢数学吗 输入描述 第一行一个正整数k(1<k<1e12) 输出描述 输出一行一个整数表示答案 示例1 输入 1 输出 1 示例2 输入 3 输出 10 思路 是等于,将整个…

【itext7】itext7操作PDF文档之添加段落文本内容、添加List列表、添加Image图片、添加Table表格

这篇文章&#xff0c;主要介绍itext7操作PDF文档之添加段落文本内容、添加List列表、添加Image图片、添加Table表格。 目录 一、itext7操作PDF内容 1.1、添加段落文本内容 1.2、添加列表内容 1.3、添加图片 1.4、添加表格 &#xff08;1&#xff09;列宽采用点单位&#…

MySQL之索引(入门级讲解)

目录 一.索引的概念 1.1索引的简介 1.2.索引的优缺点 二.MySQL索引语法 2.1查看索引 2.2创建索引 2.2.1 创建表时创建索引 2.2.2存在的表上创建索引 2.3删除索引 三.索引的数据结构 3.1Btree索引 3.2Hash索引 3.4Hash索引和Btree索引的对比 &#x1f381;个…

基于Kitti数据集的智能驾驶目标检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于Kitti数据集的智能驾驶目标检测系统可用于日常生活中检测与定位行人&#xff08;Pedestrian&#xff09;、面包车&#xff08;Van&#xff09;、坐着的人&#xff08;Person Sitting&#xff09;、汽车&#xff08;Car&#xff09;、卡车&#xff08;Truck…

[2023新教程]M2 Mac安装Anaconda和Pytorch

本文首发于我的学习之路&#xff1a;https://liguang.wang/index.php/archives/80/ 1 背景 最近使用起了Mac Mini&#xff0c;需要安装Anaconda和Pytorch。网上的教程非常老旧&#xff0c;可读性较差&#xff0c;于是记录下自己的安装过程&#xff0c;分享给大家。 2 详细过…

二十三章:Gated-SCNN:Gated Shape CNNs for Semantic Segmentation ——用于语义分割的门控形状卷积神经网络

0.摘要 当前图像分割的最先进方法通过一个密集的图像表示来处理颜色、形状和纹理信息&#xff0c;这些信息都在深度卷积神经网络内部进行处理。然而&#xff0c;这种方法可能不太理想&#xff0c;因为它们包含了与识别相关的非常不同类型的信息。在这里&#xff0c;我们提出了一…

百度文心一言文心千帆大模型 ERNIE-Bot-turbo调用示例(golang版本)

百度的文心一言推出来也有一段时间了&#xff0c;但是接口部分一直没有公开&#xff0c;需要进行申请 最近&#xff0c;有朋友提供了文心千帆大模型的api权限&#xff0c;拿到了必须的参数&#xff0c;现在就来测试一下 下面是使用golang封装的文心千帆 ERNIE-Bot-turbo模型的调…

2023.7新版Pycharm没有Manager Repository(消失)下载镜像源问题

一、旧版本操作无法找到Manager Repository 二、解决方法---点击左侧键 三、点击添加设置小按钮 四、点击号复制进去镜像源网站即可 五、给大家推荐的镜像源网站 这里清华大学的镜像速度是最快的 https://pypi.tuna.tsinghua.edu.cn/simple/ 清华大学镜像http://pypi.douban…

天然气管道压力监测系统:管网安全守护者

随着我国工业的不断发展和能源需求的日益紧缺&#xff0c;天然气作为一种清洁能源正被广泛地开发和利用。然而。每年因天然气泄漏爆炸而造成的事故层出不穷&#xff0c;不仅威胁着人民的生命安全&#xff0c;还会对环境造成严重污染。面对这样的安全隐患&#xff0c;及时采取措…

Orangepi docker Home Assistant部署

手册上多数应用在树莓派用过&#xff0c;发现一个Home Assistant之前没搞过 按照手册先安装docker&#xff0c;使用enable_docker.sh 然后使用 docker run hello-world 来测试docker是否成功&#xff0c;如果hello from docker&#xff0c;即正常 通过以下docker命令搜索hom…

css - display属性

display属性有四个取值&#xff1a;block inline inline-block none 1、block display是类似于h和p标签在网页中默认显示方式&#xff0c;占据整整一行&#xff0c;所以不会与其他元素共同显示在一行之内。常见的block-display元素还有div、ol ul li、form等。如果想对某个blo…

【PHP面试题80】Redis消息发布与订阅功能怎么用的?

文章目录 &#x1f680;一、前言&#x1f680;二、PHP中安装Redis扩展&#x1f680;三、Redis消息发布与订阅&#x1f50e;3.1 创建发布者&#x1f50e;3.2 创建订阅者&#xff1a;&#x1f50e;3.3 运行发布者和订阅者&#xff1a; &#x1f680;四、总结 &#x1f680;一、前…

tomcat 使用部署

--烛光照亮了晚餐&#xff0c;照不出个答案。 不BB。 --部署web项目&#xff08;无 maven 纯web项目&#xff09; 直接把整个项目放到tomcat的webapps目录的下面然后重启tomcat即可 --部署html静态项目 直接把静态html放到tomcat的webapps目录下面即可&#xff0c;不要重命…

spring中bean实例化的三种方式 -- Spring入门(二)

文章目录 前言1.Bean实例化简介2.bean的实例化 -- 构造方法3.bean的实例化 -- 静态工厂实例化4.bean实例化 -- 实例工厂和FactoryBean5.三种bean实例化方式的区别 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。…

JDBC之用IDEA连接SQLServer数据库实用教程

目录 前言 一、数据库准备 1.通过SQLServer配置管理器打开TCP/IP协议&#xff0c;设置状态为已启用 2.数据库登录模式设置为身份验证登录&#xff08;如果已经设置可忽略此步骤&#xff09; 3.下载SQLServer驱动jar包 二、IDEA准备 1.导入SQLServer驱动jar包 2.编写测试代…

【UE5 多人联机教程】02-开始游戏菜单控件

目录 步骤 一、完善开始游戏菜单控件 二、控件功能实现 2.1 “开始游戏”按钮切换界面 2.2 “创建房间”、“加入房间”按钮切换界面 2.3 “创建房间”按钮 步骤 一、完善开始游戏菜单控件 打开“UMG_Main” 首先在控件切换器外层包裹一个画布面板 然后调整控件切换器全…

Docker-volume【基础使用】

目录 【1】将本地的目录挂载到容器中 【2】volume卷&#xff0c;持久化 【3】volume数据共享 【4】volume持久化的作用 【5】volume - 随机卷的使用方式 【6】 启动nginx容器访问80出现HELLO&#xff0c;访问81出现WORLD 【1】将本地的目录挂载到容器中 1、容器的数据目录…

第112天:免杀对抗-加载器分离无文件落地图片隐写SOCK管道参数协议化

知识点 #知识点&#xff1a; 1、无文件落地&分离拆分-将shellcode从文本中提取-file 2、无文件落地&分离拆分-将shellcode与加载器分离-argv 3、无文件落地&分离拆分-将shellcode远程协议加载-http 4、无文件落地&分离拆分-将shellcode通过管道传输-socket 5、…