在vue中子组件修改props引发的对js深拷贝和浅拷贝的思考
不管是react还是vue,父级组件与子组件的通信都是通过props来实现的,在vue中父组件的props遵循的是单向数据流,用官方的话说就是,父级的props的更新会向下流动到子组件中,反之则不行。也就是说,子组件不应该去修改props。但实际开发过程中,可能会有一些情况试图去修改props数据:
1、这个props只是传递一个初始值,子组件把它当做一个局部变量来使用,这种情况一般定义一个本地的data属性,将props的值赋值给它。如下:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
2、这个props的值以原始数据传入,但是子组件对其需要转换。这种情况,最好使用computed来定义一个计算属性,如下:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
以上两种情况,传递的值都是基本数据类型,但是大多数情况下,我们需要向子组件传递一个引用类型数据,那么问题就来了。
JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
比如,在父组件中有一个列表,双击其中一个元素进行编辑,该元素的数据作为props传递给一个子组件,在子组件中需要对该数据进行编辑,你会发现如上所说,编辑后父组件的值也发生了变化。实际上我们想父组件影响子组件,但是子组件修改不要影响父组件。vue官网上貌似没说明这种情况应该如何处理。

这里情况相对简单点,在传递props时用Object.assign拷贝一份数据(这里数据是一个单层级对象),然后在子组件里面对其进行编辑。Object.assign能实现对象的合并,但是它是浅拷贝,也就是说如果对象的熟悉也是对象就不行。
于是查阅了相关资料,再次巩固下JS中深拷贝与浅拷贝的相关知识。
1、基本数据类型和引用数据类型的存储位置
基本数据类型是存储在栈内存中,比如 var a=1;

当进行复制操作b=a时,会在栈内存中再开一个内存,如下

变量a和变量b的存储互补影响,如果此时修改b的值不会影响a的值。
引用类型数据存储在堆内存中,引用类型的名是存储在栈内存中,值是存储在堆内存中,但是栈内存会提供引用地址指向堆内存中的值。

当进行b=a的复制操作时,复制的是引用地址,而不是堆内存中的值。

而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

而实际上我们希望的效果应该是这样:

好,到这里,到底什么是深浅拷贝:
对于仅仅是复制了引用(地址),换句话说,复制了之后,原来的变量和新的变量指向同一个东西,彼此之间的操作会互相影响,为 浅拷贝。
而如果是在堆中重新分配内存,拥有不同的地址,但是值是一样的,复制后的对象与原来的对象是完全隔离,互不影响,为 深拷贝。
回顾下JS里实现拷贝的方法有哪些:
针对数组有这些方法:
Array.slice()
var a=[1,2,3];
var b=a.slice();
b[0]=4;
console.log(b);//[4,2,3]
console.log(a);//[1,2,3]
Array.concat
var a=[1,2,3];
var b=a.concat();
b[0]=4;
console.log(b);//[4,2,3]
console.log(a);//[1,2,3]
当然,也可以遍历数组赋值。
但是以上两种只对单级结构的数组有效,如果数组的元素是一个引用类型,就不行了,比如:
let a=[0,1,[2,3],4],
b=a.slice();
a[0]=1;
a[2][0]=1;
console.log(a,b);

修改二维数组的元素还是会影响原数组,也就是说slice和concat实际上是浅拷贝。
针对对象:
Object.assign()
var a={
"name":"张三"
};
b=Object.assign({},a);
b.name="李四";
console.log(b.name);//李四
console.log(a.name);//张三
同样该方法也是浅拷贝,如果对象属性值是引用类型也不行;
那么到底有哪些办法可以实现深拷贝呢
1、递归
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[,,,],
b=deepClone(a);
a[]=;
console.log(a,b);
2、jquery中的$.extend();
var obj = {name:'xixi',age:,company : { name : '腾讯', address : '深圳'} };
var obj_extend = $.extend(true,{}, obj); //extend方法,第一个参数为true,为深拷贝,为false,或者没有为浅拷贝。
console.log(obj === obj_extend);
obj.company.name = "ali";
obj.name = "hei";
console.log(obj);
console.log(obj_extend);
3、JSON对象的JSON.parse()和JSON.stringify();
var obj = {name:'xixi',age:,company : { name : '腾讯', address : '深圳'} };
var obj_json = JSON.parse(JSON.stringify(obj));
console.log(obj === obj_json);
obj.company.name = "ali";
obj.name = "hei";
console.log(obj);
console.log(obj_json);
4、Lodash中的_.cloneDeep()
var objects = [{ 'a': }, { 'b': }];
var deep = _.cloneDeep(objects);
console.log(deep[] === objects[]);
// => false
虽然通过拷贝props数据解决了问题,但是拷贝后修改新数据的属性并不会触发vue的更新机制,需要强制更新$forceUpdate(),总觉得很奇怪,不知道大家有什么更好的办法没有,欢迎大家留言讨论。
参考文章:
https://zhuanlan.zhihu.com/p/26282765
https://zhuanlan.zhihu.com/p/26282765
在vue中子组件修改props引发的对js深拷贝和浅拷贝的思考的更多相关文章
- vue中直接修改props中的值并未给出警告,为啥?
问:vue中直接修改props中的值并未给出警告,为啥? 答:如果props传入的值是引用类型,在子组件中改变其元素,不改变引用,那么不报错: 如果是基本类型,那么在修改时浏览器控制台会有报错信息. ...
- Vue中子组件调用父组件的方法
Vue中子组件调用父组件的方法 相关Html: <!DOCTYPE html> <html lang="en"> <head> <meta ...
- vue中子组件的methods中获取到props中的值
这个官网很清楚,也很简单,父组件中使用v-bind绑定传送,子组件使用props接收即可 例如: 父组件中 <template> <div> <head-top>& ...
- vue中子组件的created、mounted钩子中获取不到props中的值问题
父子组件通信 这个官网很清楚,也很简单,父组件中使用v-bind绑定传送,子组件使用props接收即可 例如: 父组件中: <template> <div> <head- ...
- vue 父子组件通信props/emit
props 1.父组件传递数据给子组件 父组件: <parent> <child :childMsg="msg"></child>//这里必须要 ...
- 【Vue】组件watch props属性值
转载: https://www.cnblogs.com/mqxs/p/8972368.html #HTML <div id="example"> <p> & ...
- Vue 父子组件传值 props
1.Vue 的渲染周期: vue 如何实现响应式追踪. 父子组件通信有很多方式,今天单独聊下props 的方式.我们通过查找官方文档很容发现,props传值有静态和动态两种传值方式. 当然props ...
- vue中子组件的拆分 父组件与子组件之间的传值
vue是组件式开发,尽量独立出子组件 prop():父组件传值给子组件 $emit():子组件传值给父组件 子组件中的设置: 使用bind <template> : default-che ...
- Vue中子组件数据跟着父组件改变和父组件数据跟着子组件改变的方法
一,子组件数据跟着父组件改变 父组件的代码 <template> <div class="home"> <img alt="Vue logo ...
随机推荐
- redis内存监控与回收
Redis有自己的内存分配器,当key-value对象被移除时,Redis不会马上向操作系统释放其占用内存.redis之所以这样的设计有两个原因. OS可能会将释放内存交换到虚拟内存,但OS的虚拟内存 ...
- NSCache 的好处
相较于 NSDictionary 线程安全 系统资源将要耗尽时,自动删减缓存 自动删减"最久未使用的"对象 不会自动拷贝键.因为有些键不支持拷贝操作 可以和 NSPurgeable ...
- 使用sqlyog连接到服务器数据库,实现可视化数据操作。(完美解决版。)《亲测!!!!》
服务器中的表 select Host ,User ,Select_priv ,Insert_priv ,Update_priv ,Delete_priv ,Create_priv ,Drop_pr ...
- J02-Java IO流总结二 《概述》
1 流的概念 流是一个信息的通道,通过通道可以访问通道连接的文件对象. 2 流的分类 根据流中数据的流向,可分为输入流和输出流 输入流:从其他的地方流入到程序内存中的,比如InputStream. ...
- (转)MySQL 线程池内幕
原文:http://blog.jobbole.com/109695/ http://mysqllover.com/?p=826 http://blog.csdn.net/hsuxu/article/d ...
- sql server 2012 数据库日志文件过大,怎么缩小?
最近发现网站不能访问,原因数据库服务器磁盘剩余空间没了.再细查发现日志文件占用了70%,收缩日志文件失败. 在网上查找原因,是没有备份不能收缩日志文件. 临时解决的方式: 备份事务日志,再收缩日志文件 ...
- C++运算符重载三种形式(成员函数,友元函数,普通函数)详解
首先,介绍三种重载方式: //作为成员函数重载(常见) class Person{ Private: string name; int age; public: Person(const char* ...
- hanlp的基本使用--python(自然语言处理)
hanlp拥有:中文分词.命名实体识别.摘要关键字.依存句法分析.简繁拼音转换.智能推荐. 这里主要介绍一下hanlp的中文分词.命名实体识别.依存句法分析,这里就不介绍具体的hanlp的安装了,百度 ...
- 仿制淘宝sku点击效果
1.依赖jquery,主要利用二维数组. 2.原生手写. 代码如下: <!DOCTYPE html> <html> <head> <meta charset= ...
- Nginx安装图片模块出错,提示fatal error: curl/curl.h
获得安装包,从网上直接下载下载地址:https://curl.haxx.se/download.html 然后解压安装后就可以了 # # cd curl- # ./configure # make & ...