我们都知道Vue作为一个轻量级的低门槛入门前端框架,其核心就是组件化开发。Vue就是由多个组件组建成的,组件化是它的精髓,更是它的强大之处。各组件实例之间是相互独立的,这也就意味着不同组件之间数据是无法相互共用。
但实际项目开发中,我们时常需要其他组件的数据,为此诞生了组件通信的问题。针对组件之前的关系:父子、兄弟、子孙的不同选用的数据传输方式也不一样,今天就来做个总结。
Vue2组件通信
父组件向子组件传值
props
父组件以属性的方式传值给子组件
子组件通过props方式接收数据
在父组件中引入子组件并绑定fatherData自定义属性
<Son:fatherData="fatherData"></Son><script>importSonfrom'@/components/son'exportdefault{name:'Father',components:{Son},data(){return{fatherData:'我是父组件向子组件传递的值-props方式'}}}</script>
在子组件中使用props接收父组件传递的数据,props里的名字跟父组件定义的属性名一致
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>
因为Vue的单向数据流
机制,子组件不能够直接去修改父组件传递的值修改的,否则能改的话那父组件的值就被“污染”了。
但是子组件内想要修改父组件传过来的值却不“污染”父组件的话,可以在子组件内定义一个变量mydata去接收fatherData数据,并使用watch监听fatherData数据的变更。
子组件向父组件传值
$emit()
子组件绑定自定义事件
使用$emit()触发更改数据
<el-button@click="handleEmit">告诉父组件我要更改数据啦</el-button><script>exportdefault{name:'Son',methods:{handleEmit(){this.$emit('triggerEmit','我是来自子组件的数据')}}}</script>
父组件定义并绑定子组件传递的triggerEmit事件
triggerEmit事件名需跟子组件$emit()的事件名一致
<Son@triggerEmit="changeData"></Son><script>importSonfrom'@/components/son'exportdefault{name:'Father',components:{Son},methods:{changeData(name){console.log(name)//=>我是来自子组件的数据}}}</script>
使用场景:通用组件更改内部的数据时告知父组件此时数据状态。
兄弟之前传值
$emit
和props
结合的方式
父组件引入两个子组件
父组件充当一个桥梁作用
<childA:myName="name"></ChildA><ChildB:myName="name"@changeName="editName"></ChildB>exportdefault{data(){return{name:'初级前端仔'}},methods:{editName(name){this.name=name}}}
在子组件B中接收变量和绑定触发事件
<p>姓名:{{myName}}</p><button@click="changeName">修改姓名</button><script>exportdefault{props:{myName:String},methods:{changeName(){this.$emit('changeName','公众号:初级前端仔')}}}</script>
父组件接受到B组件的changeName事件,就会执行editName事件并修改name的数据,利用响应式机制,父组件就会将最新的name值传递给组件A,组件A渲染最新的name值
<p>姓名:{{newName}}</p><script>exportdefault{props:{myName:String}}</script>
至今组件B的更改就通过父组件传达到给组件A完成数据的传递
bus方式
创建一个公共的bus.js文件
暴露出Vue实例
importVuefrom"vue"exportdefaultnewVue()
在需要组件通信的组件中都引入该文件
<template><div><div>我是通信组件A</div><el-button@click="changeName">修改姓名</el-button></div></template<script>import{EventBus}from"../bus.js"exportdefault{data(){return{}},methods:{changeName(){EventBus.$emit("editName",'初级前端仔,点点关注!')}}}</script>
另外一个组件中同样引入bus.js文件,通过$on监听事件回调
<template><div>我是通信组件B</div></template<script>import{EventBus}from"../bus.js"exportdefault{data(){return{}},mounted:{EventBus.$on('editName',(name)=>{console.log(name)//=>初级前端仔,点点关注!})}}</script>
通过引入bus.js文件,使得EventBus获得Vue实例,其实是跟上一种方式是一样的。
直接访问组件实例
parent/children
子组件通过$parent获得父组件实例
父组件通过$children获得子组件实例数组
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>0
由图可知this.$parent可以获取到父组件的方法、data的数据等,并可以直接使用和执行。
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>1
正如你所看到的,父组件是在mounted()生命周期中获取子组件实例的,并且获取的实例是一个数组形式
所以需要this.$children[0]
才可以获取某个组件实例,并调用组件方法和数据
$refs
父组件使用$refs获得组件实例
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>2
这样父组件就可以直接使用this.$refs.xx
获取子组件的实例了
多组件或深层次组件通信
provide/inject(提供/注入)
父组件使用provide注入数据
子组件使用inject使用数据
例如,如果我们有这样的层次结构:
那么我们的provide/inject就可以派上用场了
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>3
至此provideName这个变量可以提供给它其下的所有子组件,包括曾孙、孙子组件等,只需要使用inject就能获取数据
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>4
这个方法的好处是:
父组件不需要知道哪个组件使用它提供出去的数据
子附件不需要知道这个数据从哪里来
vuex状态管理
相当于一个公共数据的仓库
提供一些方法管理仓库数据
关于这个的话直接去看官网吧 vuex[https://vuex.vuejs.org/zh]
总结
至此vue2中组件通信的方式就总结完了,主要分为三大类:
. 父子通信:父向子传递数据通过props,子向父传递数据通过$emit事件,父链/子链使用parent/children,直接访问组件实例用refs
. 兄弟通信:bus、Vuex
. 跨级通信:bus、Vuex、provide/inject
Vue3组件通信
props和emit
setup函数可以接受两个参数,prop和context,其中context可以解构出emit实例
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>5
Vue3中没有this的概念了,所以就不会有this.$emit存在,所以可以从setup传入的context结构出emit实例,从而派发事件给父组件
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>6
ref
Vue3我们可以从Vue中导出ref方法,得到子组件的实例
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>7
通过,在子组件声明ref属性,属性值必须和const btnRef = ref(null)这里声明的变量名一致,否则会报错,拿到子组件实例后就可以直接调用组件的sendParent方法了
这里使用的btnRef.value?.是可选链操作符语法,代表?
前面的值为true才继续执行后面的语句,感兴趣的小伙伴可以点击去看看,这里就不赘叙了
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>8
子组件只提供一个方法,供父组件获取实例后执行,也可以执行更为复杂的数据通信
provide/inject
这里的用法跟Vue2差异不大,直接上案例吧
<template><div>我是父组件的数据:{{fatherData}}</div><div>我是父组件传递修改后的数据:{{mydata}}</div></template><script>exportdefault{name:'Son',props:{fatherData:{type:String,default:''}}data(){mydata:'公众号:初级前端仔'+this.fatherData},watch:{fatherData(newVal){this.mydata='公众号:初级前端仔'+newVal}},}</script>9
直接使用provide将需要传递的数据注入,不需要考虑谁去获取
<el-button@click="handleEmit">告诉父组件我要更改数据啦</el-button><script>exportdefault{name:'Son',methods:{handleEmit(){this.$emit('triggerEmit','我是来自子组件的数据')}}}</script>0
子孙组件使用inject获取到父组件注入的数据