前言
之前学习组件间通信,网上直接来一大堆,什么子父组件通信、兄弟组件通信、祖孙组件通信等,接着直接列举八种方法,一一去学吧。但是我感觉学完之后老是忘记。之后通过看教程,查阅文档,我用了一种独特的方式去学习组件间通信,我个人感觉通过这种方式,我还是记得比较清晰的,特意来给大家分享一下。
主要思想是这样:你站在当前组件的角度,去向父、祖、子、其它组件传值和取值。
Vue组件通信方式
首先还是简单介绍一下组件通信的几种方式吧,主要分为以下几种(还是以当前组件为视角):
- 当前组件与父组件、子组件通信(也就是父子组件通信)。
- 当前组件与祖组件通信(祖孙组件通信)。
- 当前组件与兄弟组件通信(兄弟组件通信)。
- 当前组件与无关系组件通信(非关系型组件通信)
可以抽象为一幅图。可能有些难懂,后续会配合代码做具体讲解
总结为:站在你当前组件的视角,你如何向父、祖、子、其它组件要值和传值。
1、当前组件->父组件
1.1、$emit
方式
向Father组件
传值,需要在Current组件
通过$emit()
将数据传递过去,$emit()
接受两个参数(this.$emit('transMessage', this.transval)
);
- 第一个参数为事件名,这个值要和
Father组件
中绑定在Current组件
上的名字对应<Current @transMessage="setdata"></Current>
(@transMessage
)。 - 第二个参数是传递到父组件的数据。
为了节省空间,省略了所有样式代码
Curret组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件,传递值为{{transval}}</h2>
<el-button @click="transData">传值</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
nema:'Current',
transval:'hello,i am current'
};
},
methods: {
transData:function(){
// 点击'传值'按钮后,会触发trans这个自定义事件,
this.$emit('transMessage', this.transval);
}
},
};
</script>
Father组件
代码
<template>
<div>
<div class="father">
<h2>Father,即将接收值为{{val}}</h2>
</div>
<div>
<Current @transMessage="setdata"></Current>
</div>
</div>
</template>
<script>
import Current from './Current.vue';
export default {
components:{
Current
},
data() {
return {
val:'Father'
};
},
methods:{
setdata(par){
console.log(par);
this.val = par
}
}
};
</script>
运行实例: 点击传值之后,
Father组件
接收到Current组件
的transval
值。
1.2、$refs
方式
在Current组件
上添加ref
属性,通过$refs
取到Current组件
这个实例对象,从而取到Current组件
中的transval
值。
Father组件
代码
<template>
<div>
<div class="father">
<h2>Father,接收值为{{val}}</h2>
</div>
<div>
<Current ref="Curall"></Current>
</div>
</div>
</template>
<script>
import Current from './Current.vue';
export default {
components:{
Current
},
data() {
return {
val:null
};
},
mounted() {
this.val = this.$refs.Curall.transval
},
};
</script>
Current组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件,传递值为{{transval}}</h2>
</div>
</div>
</template>
<script>
export default {
data() {
return {
nema:'Current',
transval:'hello,i am current'
};
}
};
</script>
运行实例:
1.3 $children
方式
在Current组件
中,通过$children
获取到Child组件
,之后可以获取到Child组件
中的数据。这个方式和ref
方式类似,都是取到了Child
这个组件。
Current组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件,Child组件中的val值为{{transval}}</h2>
</div>
<div>
<Child></Child>
</div>
</div>
</template>
<script>
import Child from "./Child.vue"
export default {
components:{
Child
},
nema:'Current',
data() {
return {
nema:'Current',
transval:null
}
},
mounted(){
// 用这个方式需要注意子组件的顺序问题
this.transval = this.$children[0].val
}
};
</script>
Child组件
代码
<template>
<div>
<div class="father">
<h2>这是Child组件</h2>
</div>
</div>
</template>
<script>
export default {
data() {
return {
val:"hello,i am Child"
};
},
};
</script>
运行实例:
2、当前组件->子组件
2.1、pop
方式
通过将Current组件
中的数据直接绑定到Child组件
上,Child组件
组件内通过prop
来获取数据。
Current组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件,传递值为{{transval}}</h2>
</div>
<div>
<Child :info="transval"></Child>
</div>
</div>
</template>
<script>
import Child from "./Child.vue"
export default {
components:{
Child
},
data() {
return {
nema:'Current',
transval:'hello,i am current'
};
}
};
</script>
Child组件
代码
<template>
<div>
<div class="father">
<h2>Child组件,接收值为{{info}}</h2>
</div>
</div>
</template>
<script>
export default {
data() {
return {
val:null
};
},
props:{
info:String
},
};
</script>
运行实例:
2.2 $parent
方式
在Child组件
中,通过$parent
获取到Current组件
,之后可以获取到Current组件
中的数据。
Current组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件</h2>
</div>
<div>
<Child></Child>
</div>
</div>
</template>
<script>
import Child from "./Child.vue"
export default {
components:{
Child
},
nema:'Current',
data() {
return {
nema:'Current',
transval:"hello,i am Current"
}
},
};
</script>
Child组件
代码
<template>
<div>
<div class="father">
<h2>这是Child组件,从Current获取的值为{{val}}</h2>
</div>
</div>
</template>
<script>
export default {
data() {
return {
val:null
};
},
mounted(){
this.val = this.$parent.transval
}
};
</script>
运行实例:
2.3 provide + inject
方式
此方式是通过在Current组件
中添加provide
配置项将数据传递出去,在Child组件
中添加inject
接收传递来的数据。
Current组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件</h2>
</div>
<div>
<Child></Child>
</div>
</div>
</template>
<script>
import Child from "./Child.vue"
export default {
components:{Child},
nema:'Current',
data() {
return {
nema:'Current',
transval:"hello,i am Current"
}
},
provide:{
"transval":"hello,i am Current"
}
};
</script>
Child组件
代码
<template>
<div>
<div class="father">
<h2>这是Child组件,从Current获取的值为{{transval}}</h2>
</div>
</div>
</template>
<script>
export default {
inject:["transval"]
};
</script>
运行实例:
3、当前组件->祖组件
3.1、$listeners
方法
这里有三个组件,分别为Grandparent组件
、Parent组件
、Current组件
、嵌套关系如下:Grandparent组件
中包含Parent组件
,Parent组件
包含Current组件
。
在Parent组件
添加v-on="$listeners"
。将Current组件
的事件传递到Grandparent组件
。$listeners
相当于一个中间容器。
Grandparent组件
代码
<template>
<div>
<div class="gra">
<h2>Grandparent组件,从Current组件获取的值为{{getdata}}</h2>
</div>
<Parent @transdata="getData"></Parent>
</div>
</template>
<script>
import Parent from './Parent.vue';
export default {
nema:'Grandparent',
components:{Parent},
data() {
return {
nema:'Grandparent',
getdata:null
}
},
methods:{
getData(par){
console.log("here");
this.getdata = par
}
}
};
</script>
Parent组件
代码
<template>
<div>
<div class="par">
<h2>Parent组件</h2>
</div>
<Current v-on="$listeners"></Current>
</div>
</template>
<script>
import Current from './Current.vue';
export default {
nema:'parent',
components:{Current},
data() {
return {
nema:'parent',
}
}
};
</script>
Current组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件</h2>
<el-button @click="transData">传值</el-button>
</div>
</div>
</template>
<script>
export default {
nema:'Current',
data() {
return {
nema:'Current',
transdata:"hello,i am Current"
}
},
methods:{
transData(){
this.$emit('transdata',this.transdata)
}
}
};
</script>
运行实例:
4、当前组件->孙子组件
4.1、$attrs
方式
有三个组件,分别为Current组件
、Child组件
、Grandson组件
、嵌套关系如下:Current组件
中包含Child组件
,Child组件
包含Grandson组件
。
在Current组件
中将数据传出去(<Child :info="transdata"></Child>
,将Current组件
中的transdata
数据以info
传出去),但是在Child组件
中不用:info
接收,而是采用v-bind="$attrs"
这种数据继续向下传递。因此可在``Grandson组件直接使用
$attrs.info`获取数据
Current组件`代码
<template>
<div>
<div class="parent">
<h2>Parent组件</h2>
</div>
<div>
<Current v-bind="$attrs"></Current>
</div>
</div>
</template>
<script>
import Current from "./Current"
export default {
components:{Current},
nema:'Current',
data() {
return {
nema:'Parent',
}
},
};
</script>
Child组件
代码
<template>
<div>
<div class="gra">
<h2>Grandparent组件</h2>
</div>
<div>
<Parent :info="transdata"></Parent>
</div>
</div>
</template>
<script>
import Parent from "./Parent.vue"
export default {
components:{Parent},
nema:'Grandparent',
data() {
return {
nema:'Grandparent',
transdata:"hello,i am Grandparent"
}
},
};
</script>
Grandson组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件,从Grandparent获得的值为{{$attrs.info}}</h2>
</div>
</div>
</template>
<script>
export default {
nema:'Current',
data() {
return {
nema:'Current',
}
}
};
</script>
运行实例:
4.2、provide + inject
方式
provide + inject
方式在前面用来父子之间传值,当然,它也可以用来祖孙之间传值。在Current组件
加入provide
属性,在Grandson组件
中加入inject
属性
Current组件
代码
<template>
<div>
<div class="cur">
<h2>Current组件</h2>
</div>
<div>
<Child></Child>
</div>
</div>
</template>
<script>
import Child from "./Child.vue"
export default {
components:{Child},
nema:'Grandparent',
data() {
return {
nema:'Current组件'
}
},
provide:{
"transval":"hello,i am Current组件"
}
};
</script>
Child组件
代码
<template>
<div>
<div class="child">
<h2>Child组件</h2>
</div>
<div>
<Grandson></Grandson>
</div>
</div>
</template>
<script>
import Grandson from "./Grandson"
export default {
components:{Grandson},
nema:'Current',
data() {
return {
nema:'Child',
}
},
};
</script>
Grandson组件
代码
<template>
<div>
<div class="grandson">
<h2>Grandson组件,从Current获得的值为{{transval}}</h2>
</div>
</div>
</template>
<script>
export default {
nema:'Current',
data() {
return {
nema:'Current',
}
},
inject:["transval"]
};
</script>
运行实例:
5、当前组件->兄弟组件
5.1 $emit + props方式。如果有三个组件,Parent组件
,Current(也是Child1)组件
,Child2组件
,包含关系如下代码所示:
<Parent>
<Current></Current>
<Child2></Child2>
</Parent>
具体流程是Child2组件
通过$emit
方式传值给Parent组件
,之后Parent组件
通过props
方式,改变Child2
中的数据,这种方式是间接的。
Parent组件
代码
<template>
<div>
<div class="par">
<h2>Parent组件</h2>
<div class="box">
<Current :info="val"></Current>
<Child2 @transdata="getdata"></Child2>
</div>
</div>
</div>
</template>
<script>
import Current from "./Current.vue"
import Child2 from "./Child2.vue"
export default {
components:{Current,Child2},
nema:'Parent组件',
data() {
return {
nema:'Parent组件',
val:null
}
},
methods:{
getdata(par){
console.log("hello");
this.val = par
}
}
};
</script>
Current组件
代码
<template>
<div class="cur">
<h2>Curent组件{{info}}</h2>
</div>
</template>
<script>
export default {
nema:'parent',
props:{
info:String
},
data() {
return {
nema:'parent',
}
}
};
</script>
Child2组件
代码
<template>
<div class="child2">
<h2>Child2组件</h2>
<el-button @click="transData">传值</el-button>
</div>
</template>
<script>
export default {
nema:'Current',
data() {
return {
nema:'Current',
transval:"hello,i am Child2"
}
},
methods:{
transData(){
this.$emit('transdata',this.transval)
}
}
};
</script>
运行实例:
5.2、eventBus
方式
主要思想就是创建一个空的组件,利用这个组件做中间件,在传值的组件中,用$emit()
发射事件,在接受值的组件中,用$on()
接受事件。
Parent组件
代码
<template>
<div>
<div class="par">
<h2>Parent组件</h2>
<div class="box">
<Current></Current>
<Child2></Child2>
</div>
</div>
</div>
</template>
<script>
import Current from "./Current.vue"
import Child2 from "./Child2.vue"
export default {
components:{Current,Child2},
nema:'Parent组件',
};
</script>
Current组件
代码
<template>
<div class="cur">
<h2>Curent组件,从Child2获取的值为{{val}}</h2>
</div>
</template>
<script>
import {eventBus} from "../../main.js"
export default {
nema:'parent',
data() {
return {
nema:'parent',
val : null
}
},
mounted(){
eventBus.$on("transdata",(par)=>{
this.val = par
})
}
};
</script>
Child2组件
代码
<template>
<div class="child2">
<h2>Child2组件</h2>
<el-button @click="transData">传值</el-button>
</div>
</template>
<script>
import {eventBus} from "../../main.js"
export default {
nema:'Current',
data() {
return {
nema:'Current',
transval:"hello,i am Child2"
}
},
methods:{
transData(){
eventBus.$emit('transdata',this.transval)
}
}
};
</script>
运行实例:
6、无关系组件通信
6.1 也可以用事件总线eventBus
,详情参照5.2
6.2 vuex
vuex
的使用场景一般是需要管理大量共享数据,这需要对自己的项目做出分析、权衡。如果您不打算开发大型单页应用,使用Vuex
可能是繁琐冗余的。
vuex使用步骤如下:在main.js中导入并使用store实例
- 首先下载vuex(
npm install vuex
),这里需要注意版本问题,原因大致是这样的。2022年2月7日,vue3就成为了默认版本。并且vue3成为默认版本的同时,vuex也更新到了4版本。也就是,现在如果执行npm i vuex
,安装的就是vuex4了。而vuex的4版本只能在vue3中使用。如果我们非要在vue2的项目当中使用vuex的4版本,就会出现如上图所示的报错。解决办法参照这篇文章:vuex报错解决办法: - 初始化配置vuex:
import Vue from "vue";
import Vuex from "vuex"
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
val:"hello,i am vuex data"
},
mutations:{
changeVal(state){
state.val = "hello,i am vuex data(changed)"
}
}
})
export default store
- 在main.js中导入并使用store实例
import store from './store'
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>',
store
})
- 使用
此实例包含三个组件,
Parent组件
、Currnt组件
、Other组件
。Parent组件
包含Currnt组件
与Other组件
(这里的包含关系只是为了显示方便,实际操作不涉及任何父子组件传值)。
Parent组件
代码
<template>
<div>
<div class="par">
<h2>Parent组件</h2>
<div class="box">
<Current></Current>
<Child2></Child2>
</div>
</div>
</div>
</template>
<script>
import Current from "./Current.vue"
import Child2 from "./Child2.vue"
export default {
components:{Current,Child2},
nema:'Parent组件',
};
</script>
Currnt组件
代码
<template>
<div class="cur">
<h2>Curent组件,vuex中val值为{{$store.state.val}}</h2>
</div>
</template>
<script>
export default {
nema:'Current'
};
</script>
Other组件
代码
<template>
<div class="other">
<h2>Other组件</h2>
<el-button @click="changeData">change</el-button>
</div>
</template>
<script>
export default {
nema:'other',
methods:{
changeData(){
this.$store.commit("changeVal")
}
}
};
</script>
运行实例:
7、总结
总结一下就是:
- 当前组件传值给父组件(
$emit
、父组件通过$refs
、父组件通过$children
) - 当前组件传值给子组件(
prop
、子组件通过$parent
、###eventBus
父组件provide
数据+子组件inject数据
) - 当前组件传值给祖组件(
$listeners
) - 当前组件传值给孙子组件(祖组件
provide
数据+孙子组件inject数据
) - 当前组件传值给兄弟组件(
$emit
+props
方式、eventBus
) - 当前组件传值给无关系型组件(
vuex
)
以上就是vue组件的常用通信方式,具体使用哪一种我们可以根据实际场景灵活使用。