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复杂数据类型的浅拷贝示意图:
浅拷贝
浅拷贝是拷贝引用,拷贝后的引用都是指向同一个对象的实例,彼此之间的操作会互相影响。
值得注意的是: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


猜你喜欢
- 前言:最近在学习PYQT5,感觉还挺有趣的,顺便记录一下自己的打包记录,也就当学习笔记啦,如果刚好也在学习python打包的小伙伴可以学一学
- 介绍图灵完备性(Turing completeness)是通用计算机的一个属性,它表示一个程序可以写另一个程序。比如 go test 命令:
- 平时写得多的是python,最近看了一点go,今天碰到了一个问题,和大家分享一下package mainimport "fmt&q
- 使用pycharm的时候,有时需要重命名文件,该怎么操作呢?下面小编给大家演示一下。首先准备一个要重命名的文件,如下图所示接着右键单击选择R
- 但有时候,需要当某事件触发时,我们先做一些操作,然后再跳转,这时,就要用JAVASCRIPT来实现这一跳转功能。 下面是具体的做法: 一:跳
- 关于在asp中不使用组件使得脚本sleep的办法还比较少见,可能比较好的办法是创建同步的xmlhttp request,直到获得的时间达到某
- 前言不管是做开发还是做过网站的朋友们,应该对于User Agent一点都不陌生,User Agent 中文名为用户代理,简称 UA,它是一个
- 我们有时候会需要在网上查找并下载图片,当数量比较少的时候,点击右键保存,很轻松就可以实现图片的下载,但是有些图片进行了特殊设置,点击右键没有
- 我在代码里定义了两个通道,分别用于生产端口和限制连接数,如果不限制连接数,容易被对方检测到或导致对方服务器不能正常运行。// 生产端口var
- 方法一使用以下流式代码,无论下载文件的大小如何,Python 内存占用都不会增加:def download_file(url):  
- 先来看javascript的直接写在了input上 <input name="pwuser" type="
- 原始数据TS PERIOD REQUEST STEPPED VALUE STATUS SECONDS20-DEC-16 00:00:00.0
- 如下所示:函数功能abs(x)返回一个数的绝对值。 参数可以是一个整数或浮点数。 如果参数是一个复数,则返回它的模。all(iterable
- Golang交叉编译平台的二进制文件熟悉golang的人都知道,golang交叉编译很简单的,只要设置几个环境变量就可以了# mac上编译l
- 本文实例为大家分享了python+opencv实现堆叠图片的具体代码,供大家参考,具体内容如下# import cv2# import nu
- text.pytitle = '智能金融起锚:文因、数库、通联瞄准的kensho革命'text = ''
- 随着短视频的大火,不仅可以给人们带来娱乐,还有热点新闻时事以及各种知识,刷短视频也逐渐成为了日常生活的一部分。本文以一个简单的小例子,简述如
- 1、何为计算属性:大白话讲就是计算出来的结果保存在属性当中,可以想象为缓存。<!DOCTYPE html><html la
- SQL Server是一个关系数据库管理系统,应用很广泛,在进行SQL Server数据库操作的过程中难免会出现误删或者别的原因引起的日志损
- nodejs和nginx都可以反向代理,解决跨域问题。本地服务const express = require('express