vue中组件通讯的方式多种多样,下面提几个比较常见的通讯方式(不一定全哈):
1、父组件向子组件通讯--props
总结下来就是父组件通过自定义属性传递变量,子组件通过props属性接收自定义属性映射的变量,从而达到变量传递,注意:这个传值子组件只能接收和使用,不能修改,应为vue规定这种变量传递只能是单向的,也就是所谓的单向数据流,很多框架都是单向数据流;
// 父组件中,father.vue写部分代码,全写太长了
<template>
<div class="father">
<Child :father_value="father_value"></Child>
</div>
</template>
<script>
import Child from './child.vue' // 假设是在同一目录下,主要是不想多写
export default{
data(){
return {
father_value:"我是父组件传递的值"
}
},
components:{
Child
}
}
</script>
<style>
// 样式就不写了
</style>
// 子组件 child.vue
<template>
<div class="child">
<p>{{father_value}}</p> //页面显示 “我是父组件传递的值”
</div>
</template>
<script>
export default{
props:{
father_value:{
type:String,
default:"父组件传值"
}
}
}
</script>
<style>
// 样式就不写了
</style>
2、子组件向父组件通讯--emit
上面说到父向子通讯是单向数据流,子组件无法修改父组件传递的值,那子组件怎么向父组件通讯呢?vue提供了emit方法,主要是可以调用父组件暴露到子组件的方法,既然能调用方法,那就能传递参数,这样就可以通讯了,注意:变量的对应,父组件在子组件暴露的自定义属性方法名,要与子组件调用emit方法第一个参数对应上,还有父组件fatherFunc中调用子组件传递的变量中的属性也要对应;
<!-- 父组件中,father.vue写部分代码,全写太长了 -->
<template>
<div class="father">
<Child @fatherFunc="fatherFunc"></Child>
<p>{{father_value}}</p> <!--“我是子组件传递给父组件的值” -->
</div>
</template>
<script>
import Child from './child.vue' // 假设是在同一目录下,主要是不想多写
export default{
data(){
return {
father_value:""
}
},
components:{
Child
},
methods:{
fatherFunc(params){
cosnole.log(params.value) // “我是子组件传递给父组件的值”
this.father_value = params.value
}
}
}
</script>
<style>
// 样式就不写了
</style>
<!-- 子组件 child.vue -->
<template>
<div class="child">
<button @click="handleClick">点击</button>
</div>
</template>
<script>
export default{
name:'child',
data(){
return {
child_value:"我是子组件传递给父组件的值"
}
},
methods:{
handleClick(){
this.$emit('fatherFunc', {value:this.child_value})
}
}
}
</script>
<style>
// 样式就不写了
</style>
3、组件之间的通讯--eventBus
核心就是对vue实例$on、$emit、$off三个方法的使用,实质就是将方法暴露到全局,以供全局调用从而达到通讯的目的;
// 在main.js中
Vue.prototype.$eventBus = new Vue() //全局挂载vue实例为eventBus
//以两个同级组件为例
<!-- compA 组件A-->
<template>
<div class="compA"></div>
</template>
<script>
export default {
name:'compA',
data(){
return {
compA_value:"我是组件A"
}
},
created(){
this.$eventBus.$on('compAFunc', this.compAFunc) //注册方法
},
methods:{
compAFunc(params){
console.log(params.value) // "我是组件B"
}
},
beforeDestroy(){
this.$eventBus.$off('compAFunc') //销毁方法
}
}
</script>
<style></style>
<!-- compB 组件B-->
<template>
<div class="compB">
<button @click='handleClick'>点击</button>
</div>
</template>
<script>
export default {
name:'compB',
data(){
return {
compB_value:"我是组件B"
}
},
methods:{
handleClick(){
this.$eventBus.$emit('compAFunc', {value:this.compB_value})
}
}
}
</script>
<style></style>
4、依赖注入--provide/inject
在这里就不说是父子组件了,因为provide/inject穿透力不止是父子组件,看下面代码中有father组件、child组件、grandson组件是嵌套关系,在顶层father组件中使用provide暴露了变量,使用方法如代码,在嵌套组件child组件和grandson组件中都能能过inject接收,在这只写了三层嵌套,如果组件有三十层嵌套,顶层通过provide暴露了,里边的29层都能通过inject接收,当这是打举个例子,应该没有组件会嵌套三十层;这就是依赖注入的使用方式;
<!-- 父组件中,father.vue写部分代码,全写太长了 -->
<template>
<div class="father">
<Child @fatherFunc="fatherFunc"></Child>
</div>
</template>
<script>
import Child from './child.vue' // 假设是在同一目录下,主要是不想多写
export default{
data(){
return {
father_value:"我是父组件的值"
}
},
provide(){
return {
father_value:father_value
}
},
components:{
Child
}
}
</script>
<style>
// 样式就不写了
</style>
<!-- 子组件 child.vue -->
<template>
<div class="child">
<p>{{father_value}}</p> // 我是父组件的值
<Grandson></Grandson>
</div>
</template>
<script>
import Grandson from './grandson.vue'
export default{
name:'child',
inject:['father_value'],
components:{
Grandson
},
data(){
return {}
}
}
</script>
<style>
// 样式就不写了
</style>
<!-- 孙子组件 grandson.vue -->
<template>
<div class="grandson">
<p>{{father_value}}</p> // 我是父组件的值
</div>
</template>
<script>
export default{
name:'grandson',
inject:['father_value']
data(){
return {}
}
}
</script>
<style>
// 样式就不写了
</style>
5、获取组件实例--refs
通过ref给组件设标志,通过refs获取组件实例对象,可以用实例对象获取内部变量以及调用内部方法;这种方式比较直接暴力,所以这种方式一般不是用来组件通讯,感觉有点杀鸡用牛刀了,通讯方式有很多,没必要用这个,下面$parent获取父组件实例也是一样的
// 父组件中,father.vue写部分代码,全写太长了
<template>
<div class="father">
<Child ref="childRef"></Child>
<button @click='handleClick'>点击</button>
</div>
</template>
<script>
import Child from './child.vue' // 假设是在同一目录下,主要是不想多写
export default{
data(){
return {
father_value:"我是组件的值"
}
},
components:{
Child
},
methods:{
handleClick(){
let childInstance = this.$refs['childRef']
console.log(childInstance.child_value)
childInstance.childFunc(father_value); // "我是父组件传递的值:我是组件的值"
}
}
}
</script>
<style>
// 样式就不写了
</style>
// 子组件 child.vue
<template>
<div class="child">
</div>
</template>
<script>
export default{
data(){
return {
child_value:"我是子组件的值"
}
},
methods:{
childFunc(value){
console.log("我是父组件传递的值:"+value)
}
}
}
</script>
<style>
// 样式就不写了
</style>
6、获取父组件--$parent
其实和上面ref获取实例差不多,vue内部将当前子组件的父组件通过$parent暴露出来了,在子组件中可以直接获取,通过组件实例可以获取父组件的变量以及调用方法,还是不建议使用这种方式来实现通讯;提一嘴,vue还暴露根组件的实例$root
// 父组件中,father.vue写部分代码,全写太长了
<template>
<div class="father">
<Child></Child>
</div>
</template>
<script>
import Child from './child.vue' // 假设是在同一目录下,主要是不想多写
export default{
data(){
return {
father_value:"我是组件的值"
}
},
components:{
Child
},
methods:{
fatherFunc(value){
console.log("我是父组件的方法,我被调用,这传递的参数:" + value) // 我是父组件的方法,我被调用,这传递的参数:我是子组件的值
}
}
}
</script>
<style>
// 样式就不写了
</style>
// 子组件 child.vue
<template>
<div class="child">
<button @click="handleClick">点击</button>
</div>
</template>
<script>
export default{
data(){
return {
child_value:"我是子组件的值"
}
},
methods:{
handleClick(){
console.log(this.$parent.father_value) //我是组件的值
this.$parent.fatherFunc(child_value)
}
}
}
</script>
<style>
// 样式就不写了
</style>
7、作用域插槽--slot
先说一下插槽,插槽是vue提供的内部定义的组件<slot></slot>,全局都可以使用,作用是增加自定义组件的灵活性,可以在自定义组件中进行占位,调用自定义组件时可以自定组件之间增加不同的template,插槽分为匿名插槽、具名插槽以及作用域插槽,在这就只说一下作用域插槽;子组件可以通过插槽向父组件传递状态,子组件会将slot上定义的属性全部都暴露在slot-scope定义的形参中;其实这种通讯方式比较少用
// 父组件中,father.vue写部分代码,全写太长了
<template>
<div class="father">
<Child>
<template slot-scope="scope">
{{scope.data.value}} // 我是子组件的值
</template>
</Child>
</div>
</template>
<script>
import Child from './child.vue' // 假设是在同一目录下,主要是不想多写
export default{
data(){
return {
father_value:"我是组件的值"
}
},
components:{
Child
}
}
</script>
<style>
// 样式就不写了
</style>
// 子组件 child.vue
<template>
<div class="child">
<slot :data={value:child_value}></slot>
</div>
</template>
<script>
export default{
data(){
return {
child_value:"我是子组件的值"
}
}
}
</script>
<style>
// 样式就不写了
</style>
8、状态管理--vuex
这玩意要单独写一篇文章才能说清楚,vuex是vue官方指定建议使用的状态集中管理插件,状态管理不是vue框架独有,react、svelte、小程序等都有;就是项目体系太大,通过一般的通讯方式太过麻烦,所以就将这个全局都可能用到的变量进行集中管理,然后全局都可以使用,vuex主要就是干这个的,而且这些变量还具有响应式;后面我独立写一篇文档说明vuex的使用方式。