Vue父子组件通讯有几种情况,方式是怎样?
Admin 2022-10-19 群英技术资讯 265 次浏览
今天我们来学习关于“Vue父子组件通讯有几种情况,方式是怎样?”的内容,下文有详解方法和实例,内容详细,逻辑清晰,有需要的朋友可以参考,希望大家阅读完这篇文章后能有所收获,那么下面就一起来了解一下吧。
父子组件通讯,可分为两种情况:
1. 父组件向子组件中传递数据
2. 子组件向父组件中传递数据
一般情况下, 1中情况可通过props解决数据传递的问题, 这里就不多赘述了。
主要谈谈2中情景的实现,有三种方式:
一. 通过props,父组件向子组件中传递数据和改变数据的函数,通过在子组件中调用父组件传过来的函数,达到更新父组件数据(向父组件传递数据)的作用(子组件中需要有相应的响应事件)
二. 通过在子组件中触发一个 自定义事件(vm.$emit),将数据作为vm.$emit方法的参数,回传给父组件用v-on:[自定义事件]监听的函数
三.通过ref对子组件做标记,父组件可以通过vm.$refs.[子组件的ref].[子组件的属性/方法]这种方式直接取得子组件的数据
下面我将一 一展示
这里就不做代码展示了
一来是因为相对比较简单
二来是因为这种方式显然不是Vue中的最佳实践(在react中倒比较常见)
想要看代码的话可以看这里:《【Vue】浅谈Vue不同场景下组件间的数据交流》http://www.cnblogs.com/penghuwan/p/7286912.html (在兄弟组件的数据交流那一节)
我们可以在子组件中通过$emit(event, [...参数])触发一个自定义的事件,这样,父组件可以在使用子组件的地方直接用 v-on来监听子组件触发的事件, 并且可以在监听函数中依次取得所有从子组件传来的参数
例如:
在子组件中某个部分写入:
this.emit("eventYouDefined", arg);
然后你就可以在父组件的子组件模板里监听:
// 这里是父组件的Template:
<Son v-on: eventYouDefined = "functionYours" />
下面是一个实例
父组件
<template> <div id="father"> <div> 我是父组件,我接受到了: {{ text || "暂无数据" }} <son v-on:sendData="getSonText"></son> </div> </div> </template> <script> import son from "./son.vue" export default { data: function () { return { text: "" } }, components: { son: son }, methods: { getSonText (text) { this.text = text } } } </script> <style scoped> #father div { padding: 10px; margin: 10px; border: 1px solid grey; overflow: hidden; } </style>
子组件:
<template> <div> <p>我是子组件,我所拥有的数据: {{ text }}</p> <button @click="sendData"> 发送数据 </button> </div> </template> <script> export default { data () { return { text: "来自子组件的数据" } }, methods: { sendData () { this.$emit("sendData", this.text) } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> button { float: left } </style>
在点击子组件中的“发送数据”按钮前, 父组件还没有接受到数据(text为空字符串), 则通过 {{ text || "暂无数据" }}将显示默认文本:‘暂无数据"
点击“发送数据”按钮后:
因为sendData自定义事件被触发,通过
this.$emit("sendData", this.text) //此处的this指向子组件实例)
子组件的text数据被父组件中:
<son v-on:sendData="getSonText"></son>
中的getSonText函数作为参数接传参受到, 从而完成了从子组件向父组件中的传参过程
对于我们上面讲的一和二的处理情景来说,有个局限性就是它们都需要以事件机制为基础(无论是像click那样的原生事件还是自定义事件),而在事件发生的时候才能调用函数将数据传递过来
但如果子组件里没有类似“按钮”的东西,因而无法制造原生事件,同时也没办法找到一个触发自定义事件的时机的时候,怎么从子组件向父组件传递数据呢??
这个时候, 我们就只能从父组件中“直接取”子组件的数据了,借助ref属性
ref是我们经常用到的Vue属性,利用它可以简单方便地从本组件的template中取得DOM实例,而实际上,如果你在父组件中为子组件设置ref的话, 就可以直接通过vm.$refs.[子组件的ref].[子组件的属性]去拿到数据啦,例如:
父组件:
<template> <div id="father"> <div> 我是父组件,我接受到了: {{ text || "暂无数据" }} <button @click="getSonText()">接受数据</button> <son ref="son"></son> </div> </div> </template> <script> import son from "./son.vue" export default { data: function () { return { text: "" } }, components: { son: son }, methods: { getSonText () { this.text = this.$refs.son.text } } } </script> <style scoped> #father div { padding: 10px; margin: 10px; border: 1px solid grey; overflow: hidden; } </style>
子组件:
<template> <div> <p>我是子组件,我所拥有的数据: {{ text }}</p> </div> </template> <script> export default { data () { return { text: "来自子组件的数据" } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> button { float: left } </style>
demo:
尚未点击“接受数据”按钮前:
点击接受数据按钮后:
通过以上三种方式, 我想你应该能解决绝大多数父子组件通信的场景了,但让我们再仔细考虑一下上面的通信场景,就会发现它们还可能存在的问题:
从子组件向父组件传递数据时,父子组件中的数据仍不是每时每刻都同步的
但在某些特殊的需求场景下,我们可能会希望父子组件中的数据时刻保持同步, 这时候你可能会像下面这样做:
这是父组件中的template:
<son :foo="bar" v-on:update="val => bar = val"></son>
在子组件中, 我们通过props声明的方式接收foo并使用
props: { foo: [type] }
同时每当子组件中数据改变的时候,通过
this.$emit("update", newValue)
把参数newValue传递给父组件template中监听函数中的"val"。然后通过
val => bar = val
这个表达式就实现了bar = newValue. 这个时候,我们发现父组件中的关键数据bar被子组件改变(相等)了!
通过数据的双向绑定, 父(组件)可以修改子的数据, 子也可以修改父的数据
Vue提供了sync修饰符简化上面的代码,例如:
<comp :foo.sync="bar"></comp>
会被扩展为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
然后你需要在子组件中改变父组件数据的时候, 需要触发以下的自定义事件:
this.$emit("update:foo", newValue)
【注意】你可能觉得这好像和我上面提到的二中的“通过自定义事件(emit)从子组件向父组件中传递数据”的那一节的内容似乎重叠了,。
然而并不是, 两者有着父子组件关系上的不同, 下面我通过一行关键的代码证明它们的区别所在
1.在我们讲解sync的这一小节里, 自定义事件发生时候运行的响应表达式是:
<son :foo="bar" v-on:update="val => bar = val"></son> 中的 "val => bar = val"
2.在二中的“通过自定义事件从子组件向父组件中传递数据” 里,自定义事件发生时候运行的响应表达式是:
<Son v-on: eventYouDefined = "arg => functionYours(arg)" /> 中的 "arg => functionYours(arg)"
对前者, 表达式 val => bar = val意味着强制让父组件的数据等于子组件传递过来的数据, 这个时候,我们发现父子组件的地位是平等的。 父可以改变子(数据), 子也可以改变父(数据)
对后者, 你的functionYours是在父组件中定义的, 在这个函数里, 你可以对从子组件接受来的arg数据做任意的操作或处理, 决定权完全落在父组件中, 也就是: 父可以改变子(数据), 但子不能直接改变父(数据)!, 父中数据的变动只能由它自己决定
下面是一个展示demo:
父组件:
<template> <div id="father"> <div> 我是父组件 <son :wisdom.sync="wisdom" :magic.sync="magic" :attack.sync="attack" :defense.sync="defense"> </son> <p>智力: {{ wisdom }}</p> <p>膜法: {{ magic }}</p> <p>攻击: {{ attack }}</p> <p>防御: {{ defense }}</p> </div> </div> </template> <script> import son from "./son.vue" export default { data: function () { return { wisdom: 90, magic: 160, attack: 100, defense: 80 } }, components: { son: son } } </script> <style scoped> #father div { padding: 10px; margin: 10px; border: 1px solid grey; overflow: hidden; } </style>
子组件:
<template> <div> <p>我是子组件</p> <p>智力: {{ wisdom }}</p> <p>膜法: {{ magic }}</p> <p>攻击: {{ attack }}</p> <p>防御: {{ defense }}</p> <button @click="increment("wisdom")">增加智力</button> <button @click="increment("magic")">增加膜法</button> <button @click="increment("attack")">增加攻击</button> <button @click="increment("defense")">增加防御</button> </div> </template> <script> export default { props: { wisdom: Number, magic: Number, attack: Number, defense: Number }, methods: { increment (dataName) { let newValue = this[dataName] + 1 this.$emit(`update:${dataName}`, newValue) } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> button { float: left } </style>
点击前
点击增加子组件中“增加智力”按钮的时候, 父组件和子组件中的智力参数同时从90变为91
点击增加子组件中“增加膜法”按钮的时候, 父组件和子组件中的智力参数同时从160变为161
从好处上看:
1.它实现了父子组件数据的“实时”同步, 在某些数据场景下可能会使用到这一点
2.sync提供的语法糖使得双向绑定的代码变得很简单
从坏处上看:
它破环了单向数据流的简洁性, 这增加了分析数据时的难度
我们对上面的例子修改一下, 把数据包裹在一个对象中传递下来:
父组件
<template> <div id="father"> <div> 我是父组件 <son :analysisData.sync="analysisData"> </son> <p>智力: {{ analysisData.wisdom }}</p> <p>膜法: {{ analysisData.magic }}</p> <p>攻击: {{ analysisData.attack }}</p> <p>防御: {{ analysisData.defense }}</p> </div> </div> </template> <script> import son from "./son.vue" export default { data: function () { return { analysisData: { wisdom: 90, magic: 160, attack: 100, defense: 80 } } }, components: { son: son } } </script> <style scoped> #father div { padding: 10px; margin: 10px; border: 1px solid grey; overflow: hidden; } </style>
子组件
<template> <div> <p>我是子组件</p> <p>智力: {{ analysisData.wisdom }}</p> <p>膜法: {{ analysisData.magic }}</p> <p>攻击: {{ analysisData.attack }}</p> <p>防御: {{ analysisData.defense }}</p> <button @click="increment("wisdom")">增加智力</button> <button @click="increment("magic")">增加膜法</button> <button @click="increment("attack")">增加攻击</button> <button @click="increment("defense")">增加防御</button> </div> </template> <script> export default { props: { analysisData: Object }, methods: { increment (dataName) { let newObj = JSON.parse(JSON.stringify(this.analysisData)) newObj[dataName] += 1 this.$emit("update:analysisData", newObj) } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> button { float: left } </style>
demo同上
父组件的数据传递给子组件, 一般通过props实现, 而在实现“父子组件数据同步”这一需求的时候, 小伙伴们可能会发现一点: 在子组件中修改引用类型的props(如数组和对象)是可行的
1.不仅可以达到同时修改父组件中的数据(因为本来引用的就是同一个数据)
2.而且还不会被Vue的检测机制发现!(不会报错)
但千万不要这样做, 这样会让数据流变得更加难以分析,如果你尝试这样做, 上面的做法可能会更好一些
不要这样做,糟糕的做法:
父组件:
<template> <div id="father"> <div> 我是父组件 <son :analysisData="analysisData"> </son> <p>智力: {{ analysisData.wisdom }}</p> <p>膜法: {{ analysisData.magic }}</p> <p>攻击: {{ analysisData.attack }}</p> <p>防御: {{ analysisData.defense }}</p> </div> </div> </template> <script> import son from "./son.vue" export default { data: function () { return { analysisData: { wisdom: 90, magic: 160, attack: 100, defense: 80 } } }, components: { son: son } } </script> <style scoped> #father div { padding: 10px; margin: 10px; border: 1px solid grey; overflow: hidden; } </style>
子组件:
<template> <div> <p>我是子组件</p> <p>智力: {{ analysisData.wisdom }}</p> <p>膜法: {{ analysisData.magic }}</p> <p>攻击: {{ analysisData.attack }}</p> <p>防御: {{ analysisData.defense }}</p> <button @click="increment ("wisdom")">增加智力</button> <button @click="increment ("magic")">增加膜法</button> <button @click="increment ("attack")">增加攻击</button> <button @click="increment ("defense")">增加防御</button> </div> </template> <script> export default { props: { analysisData: Object }, methods: { increment (dataName) { let obj = this.analysisData obj[dataName] += 1 } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> button { float: left } </style>
关于“Vue父子组件通讯有几种情况,方式是怎样?”的内容今天就到这,感谢各位的阅读,大家可以动手实际看看,对大家加深理解更有帮助哦。如果想了解更多相关内容的文章,关注我们,群英网络小编每天都会为大家更新不同的知识。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
JS弹窗事件如何理解,弹窗的使用是怎样?弹窗事件就是在我们执行某操作的时候,弹出信息框给出提示。或收集数据的时候,弹出窗口收集信息,不想收集可以取消隐藏。
这篇文章主要介绍了vue中配置scss全局变量的步骤,帮助大家更好的理解和使用vue框架,感兴趣的朋友可以了解下
setTimeout函数设定页面延时3秒刷新或跳转,这个使用也很广泛,比如我们添加完一条数据之后,希望先弹出添加成功的标志之后再刷新列表,这个时候可能就需要使用setTimeout页面延时刷新功能,使用很简单,代码如下: //假如有个提示框layer.msg(用户添加成功
今天我们来了解一下vuex,下文会给大家分享vuex中四大对象state、mutations、getters、actions以及他们的用法,对新手学习和理解vuex对象有一定的帮助,感兴趣的朋友就继续往下看吧。
在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常用于执行未经测试或不受信任的程序或代码,它会 为待执行的程序创建一个独立的执行环境,内部程序的执行不会影响到外部程序的运行,下文我们来介绍一个“浏览器世界”的沙箱
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008