从内存角度思考vue中父组件向子组件传递对象可以直接被修改的问题
从内存角度思考vue中父组件向子组件传递对象可以直接被修改的问题
nancy Jane前言
在vue中,父子组件传值借助于props,子组件也不能直接修改父组件的值,需要使用$emit实现。当父组件向子组件传递的数据是对象时,子组件就可以直接修改了。
1.浅析JavaScript内存空间
JS的内存空间可以被分为两种:栈内存和堆内存。
(1)基本数据类型保存在栈内存中,如果删除一个栈原始数据,遵循先进后出;因为基本数据类型占用空间小,大小固定,通过按值来访问,属于被频繁使用的数据。
基本数据类型包括Boolean,Number,String,Underfined,Null以及对象变量的指针。
(2)引用数据类型保存在堆内存中,并且会有一个十六进制的内存地址,在栈内存中声明的变量的值就是十六进制的内存地址。因为引用数据类型占据空间大,大小不固定。如果存储在栈中,将会影响程序运行的性能。引用数据类型在栈中存储了该对象在堆内存的引用地址,对象的内容存储在堆中,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存的引用地址。
引用数据类型包括Object,Array,Function。
2.问题解决
(1)为什么子组件可以修改父组件传递的对象
联系到内存空间中数据存储的方式,当js访问对象数据的时候,只能通过访问栈中的引用地址,所以父组件传递给子组件的,实际上是对象的引用地址。子组件修改对象的属性值时,修改的是堆空间中的数据,所以父组件中的数据也会变化。
该对象指向的内存地址并没有改变,所以子组件修改父组件的值时并不会报错。
(2)子组件为什么不能直接修改父组件中的数据;
因为Vue采用的是单向数据流,所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警
3.子组件修改父组件数据的几种方法
(1)$emit
- 在子组件中,需要$emit发送时间(.$emit(自定义事件名,参数,……))
- 父组件中,在子组件的标签上写监听,绑定它$emit的事件
(2)使用v-model
原理也是借助于对象存储在堆地址中。将父组件传递进来的数据写成对象的数据,然后再使用v-model修改data中的数据。
1 | //子组件 |
1 | //父组件 |
(3)watch和.sync修饰符来实现
其实还是利用watch和emit实现
1 | export default{ |
1 | <template> |