Vue父子组件传值的一些坑

作者:文渊 时间:2024-04-28 09:30:57 

在用 Vue 的父子组件传值时遇到一个冷门的问题,子组件改变值后父组件的值也随之改变了,特此记录下原因和解决方式。
再系统梳理下 JavaScript 的深拷贝与浅拷贝相关知识点。

1. 问题描述

父组件传值给子组件,子组件改变传过来的值后,父组件的值也会跟着改变。
这个问题比较冷门,平时如果对组件通信使用得比较简单,一般不会遇到。

2. 原因剖析

  • 核心:双向绑定

父子组件传值的时候涉及双向绑定,当传值为 object 类型时,传值之后数据源会被改变。

  • 深拷贝与浅拷贝

下文详细讲。

3. 解决方案

我目前采用的解决办法是:
传值的时候不要直接传数据源,而是通过拷贝或者定义新变量等方式传值。

简单处理就 JSON.parse(JSON.stringify(obj)),但是这种简单粗暴的方法有其局限性。当值为 undefined、function、symbol 会在转换过程中被忽略。所以,对象值有这三种的话用这种方法会导致属性丢失。

剩下的就是自写深拷贝的工具函数,或者直接借助第三方的库函数,下面展开讲。

4. 深拷贝和浅拷贝

JavaScript中的浅拷贝与深拷贝,只是针对复杂数据类型(Object,Array)的复制问题。浅拷贝与深拷贝都可以实现在已有对象上再生出一份的作用。但是对象的实例是存储在堆内存中然后通过一个引用值去操作对象,由此拷贝的时候就存在两种情况了:拷贝引用和拷贝实例,这也是浅拷贝和深拷贝的区别。

下图为JavaScript复杂数据类型的浅拷贝示意图:

Vue父子组件传值的一些坑

  • 浅拷贝

浅拷贝是拷贝引用,拷贝后的引用都是指向同一个对象的实例,彼此之间的操作会互相影响。

值得注意的是:Object.assgin() 是浅拷贝,它只能深拷贝第一层,深层的还是浅拷贝。因为 Object.assign() 拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。(摘选自MDN)

MDN讲述 assign 的时候,就有一个典型的例子,这里是文章链接。

下面列举第一类浅拷贝 - 拷贝原对象的引用:


/**
* 对象的浅拷贝
*/
var obj1 = {
name:'wenyuan',
age: 22
}
var obj2 = obj1;
obj2['job'] = 'coder';
console.log(obj1); //Object {name: "wenyuan", age: 22, job: "coder"}
console.log(obj2); //Object {name: "wenyuan", age: 0, job: "coder"}

/* ------------------------- 华丽的分割线 ------------------------- */

/**
* 数组的浅拷贝
*/
var arr1 = [1, 2, 3, '4'];
var arr2 = arr1;
arr2[1] = "test";
console.log(arr1); // [1, "test", 3, "4"]
console.log(arr2); // [1, "test", 3, "4"]

接下来看第二类浅拷贝 - 源对象拷贝实例,其属性对象拷贝引用:

这种情况,外层源对象是拷贝实例,如果其属性元素为复杂数据类型(Object、Array)时,内层元素拷贝引用。

对源对象直接操作,不影响另外一个对象,但是对其属性操作时候,会改变另外一个对象的属性的值。


/**
* 对象的浅拷贝
* jQuery的 $.extend(a,b) 或 $.extend({},a,b)
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
 blog: 'www.wenyuanblog.com'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = $.extend({},obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出true,说明对于Object类型的属性是拷贝引用
console.log(obj1.skills === obj2.skills) // 输出true,说明对于Array类型的属性是拷贝引用

/**
* 对象的浅拷贝
* ES6的 Object.assign() 和 对象扩展运算符...
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
 blog: 'www.wenyuanblog.com'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = Object.assign({},obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出true,说明对于Object类型的属性是拷贝引用
console.log(obj1.skills === obj2.skills) // 输出true,说明对于Array类型的属性是拷贝引用
var obj3 = {...obj1};
console.log(obj1 === obj3) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.skills === obj3.skills) // 输出true,说明对于Array类型的属性是拷贝引用
console.log(obj1.skills === obj3.skills) // 输出true,说明对于Array类型的属性是拷贝引用

/* ------------------------- 华丽的分割线 ------------------------- */

/**
* 数组的浅拷贝
* Array.prototype.slice()
*/
var arr1 = [{name: "wenyuan"}, {name: "Evan You"}];
var arr2 = arr1.slice(0);
console.log(arr1 === arr2); // 输出false,说明外层数组拷贝的是实例
console.log(arr1[0] === arr2[0]); // 输出true,说明其元素拷贝的是引用

/**
* 数组的浅拷贝
* Array.prototype.concat()
*/
var arr1 = [{name: "wenyuan"}, {name: "Evan You"}];
var arr2 = arr1.concat();
console.log(arr1 === arr2); // 输出false,说明外层数组拷贝的是实例
console.log(arr1[0] === arr2[0]); // 输出true,说明其元素拷贝的是引用

/**
* 数组的浅拷贝
* ES6的 Object.assign() 和 对象扩展运算符...
* 由于数组是特殊的对象,所以ES6中的这种方式也可以用于数组
*/
var arr1 = [{name: "wenyuan"}, {name: "Evan You"}];
var arr2 = Object.assign([],arr1)
var arr3 = { ...arr1 };
console.log(arr1 === arr2); // 输出false,说明外层数组拷贝的是实例
console.log(arr1 === arr3); // 输出false,说明外层数组拷贝的是实例
console.log(arr1[0] === arr2[0]); // 输出true,说明其元素拷贝的是引用
console.log(arr1[0] === arr3[0]); // 输出true,说明其元素拷贝的是引用
  • 深拷贝

在堆中重新分配内存,并且把源对象所有属性都进行新建拷贝,以保证深拷贝的对象的引用图不包含任何原有对象或对象图上的任何对象,拷贝后的对象与原来的对象是完全隔离,互不影响。

下面列举一些深拷贝的例子:


/**
* 对象的深拷贝
* JSON.stringify()和JSON.parse()
* 这种深拷贝最简单,但有其局限性,上文已经提到过了
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
 blog: 'www.wenyuanblog.com'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出false,说明对于Object类型的属性也是拷贝实例
console.log(obj1.skills === obj2.skills) // 输出false,说明对于Array类型的属性也是拷贝实例

/**
* 对象的深拷贝
* jQuery的 $.extend(true,a,b)
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
 blog: 'www.wenyuanblog.com'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = $.extend(true,obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出false,说明对于Object类型的属性也是拷贝实例
console.log(obj1.skills === obj2.skills) // 输出false,说明对于Array类型的属性也是拷贝实例

/**
* 对象的深拷贝
* 也可以自己写一个函数实现,用递归+判断,注意别进入死循环就好
* 这里不举例了,以前我整理过一篇常用工具类函数的博客,里面包含了深拷贝函数
*/

/**
* 对象的深拷贝
* lodash的_.cloneDeep
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
 blog: 'www.wenyuanblog.com'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = _.cloneDeep(obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出false,说明对于Object类型的属性也是拷贝实例
console.log(obj1.skills === obj2.skills) // 输出false,说明对于Array类型的属性也是拷贝实例

来源:https://www.wenyuanblog.com/blogs/vue-pit-child-component-value-changes-affect-the-parent-component-value.html

标签:vue,父组件,子组件,传值
0
投稿

猜你喜欢

  • Python + selenium 自动化测试框架详解

    2021-01-15 00:06:35
  • Python numpy.transpose使用详解

    2021-10-17 16:51:10
  • H2 数据库导入CSV文件实现原理简析

    2024-01-15 12:06:27
  • vue项目中data数据之间互相访问的实现

    2024-05-28 15:51:43
  • python3实现从kafka获取数据,并解析为json格式,写入到mysql中

    2023-06-12 21:30:21
  • vue如何在for循环中设置ref并获取$refs

    2023-07-02 17:00:04
  • 在SQL触发器或存储过程中获取在程序登录的用户

    2024-01-29 09:30:40
  • Python Django框架实现应用添加logging日志操作示例

    2022-09-17 20:59:52
  • Python中弱引用的神奇用法与原理详解

    2023-01-12 04:27:08
  • 一种特别简单的MySQL数据库安装方法

    2008-12-17 15:30:00
  • python自动发邮件总结及实例说明【推荐】

    2021-05-15 04:32:04
  • Vue实现步骤条效果

    2024-04-28 10:54:23
  • Python中列表元素转为数字的方法分析

    2023-03-20 22:07:46
  • 5步让你的CSS样式表成功减肥

    2009-08-02 21:27:00
  • 模糊查询的通用存储过程

    2024-01-23 06:49:12
  • Python和C/C++交互的几种方法总结

    2021-08-25 00:49:06
  • Django实现快速分页的方法实例

    2022-12-27 06:02:30
  • 一篇文章告诉你如何用Python控制Excel实现自动化办公

    2022-02-03 17:07:59
  • JavaScript入门学习书籍的阶段选择

    2008-01-11 19:39:00
  • 让sql2005运行在独立用户下出现 WMI 提供程序错误的解决方式

    2024-01-13 13:12:50
  • asp之家 网络编程 m.aspxhome.com