乐趣区

关于vue.js:Vue-如何实现组件间通信

写在后面

组件间通信简略来说就是组件间进行数据传递。就像咱们日常的打电话,就是通信的一种形式,你把话说给我听,我把话说给你听,说的话就是数据。电话就是通信形式的一种。无论是 Vue 还是 React,都得进行组件间通信。Vue 组件间通信的形式介绍如下。

1. 父子间通信

最常见的就是父子之间的通信,通信是双向的数据传递。

1.1 父组件 –> 儿子组件

父组件向儿子组件传递数据的形式就是 通过 Prop 向子组件传递数据

//child.vue
<template>
    <div>
        我是儿子,我收到来自父亲的数据为 {{value}}
    </div>
</template>

<script>
export default {
    props:{value: String}
}
//App.vue
<template>
  <div id="app">
    <Child :value="x" />
  </div>
</template>

<script>
import Child from './components/Child'
export default {data(){
    return {x: 'hi,child'}
  },
  components:{Child}
}
</script>

1.2 儿子组件 –> 父组件

儿子组件向父组件传递数据的形式就是通过子组件内 $emit 触发自定义事件,子组件应用时 v-on 绑定监听自定义事件。

这里的 v-on 事件通信是在子组件应用时作为子组件的事件属性主动进行监听的。

因而儿子组件向父组件传递数据,依赖于子组件应用时的自定义事件属性。

//child.vue
<template>
    <div>
        我是儿子,我收到来自父亲的数据为 {{value}}
        <button @click="sayHi">
            向父组件打招呼
        </button>
    </div>
</template>

<script>
export default {
    props:{value: String},
    methods:{sayHi(){this.$emit('sayHi','hi,parent!');
        }
    }
}
</script>
//App.vue
<template>
  <div id="app">
    我是父组件,我收到子组件传来的数据为 {{y}}
    <Child :value="x" @sayHi="y = $event"/>
  </div>
</template>

<script>
import Child from './components/Child'
export default {data(){
    return {
      x: 'hi,child',
      y: ''
    }
  },
  components:{Child}
}
</script>

2. 爷孙间通信

爷孙间通信,能够应用两次 v-on 通信,爷爷爸爸通信,而后爸爸儿子通信。

也可应用下方的任意组件间通信的形式。

3. 任意组件间通信

任意组件间通信就不再辨别是 A 向 B 通信,还是 B 向 A 通信,而是通用的形式,谁想发送数据就应用对应的 API 发送数据,谁想要接管什么数据,就应用对应的 API 接管。

任意组件间通信有两种形式,一种是应用 EventBus 公布订阅模式通信,一种是应用 Vuex 通信。

3.1 EventBus

EventBus,从字面意思了解就是事件公交车,所有触发的事件传递的数据都从前门上车保留到公交车上,而后通过监听对应事件提供的进口让对应的事件数据下车。

EventBus,理论意思是公布和订阅模式,就是谁想把数据传递进来,就要通过触发自定义事件的 API 进行数据的公布;谁须要接管该数据信息的,就通过事件监听的 API 进行数据的监听,一旦检测到监听的数据公布进去,就会接管,这就是数据的订阅。

EventBus 通信形式最重要是搞明确公布和订阅的接口 API,在 Vue 中,Vue 实例有提供两个接口,即 $emit$on,因而能够新创建一个空的 Vue 实例,来取得这两个接口。

const eventBus = new Vue();
eventBus.$emit(eventName, […args]) // 公布事件
eventBus.$on(event, callback)      // 订阅事件 

实例如下:

// eventBus.js
import Vue from 'vue'
export const eventBus = new Vue();
//child
<template>
    <div>
        我是儿子,我收到来自父亲的数据为 <strong>{{value}}</strong>
        <button @click="sayHi">
            向父组件打招呼
        </button>
        <button @click="sibling">
            向兄弟组件打招呼
        </button>
    </div>
</template>

<script>
import {eventBus} from '../eventBus.js'
export default {
    props:{value: String},
    methods:{sayHi(){this.$emit('sayHi','hi,parent!');
        },
        sibling(){eventBus.$emit('sibling','hi,brother');
        }
    }
}
</script>

<style scoped>
    strong{color: red;}
</style>
//sibling
<template>
    <div>
        我是兄弟组件,我收到来自儿子组件的数据信息为 <strong>{{x}}</strong> 
    </div>
</template>

<script>
import {eventBus} from '../eventBus.js'
export default {data(){
        return {x: ''}
    },
    mounted(){eventBus.$on('sibling', (msg)=>{this.x = msg;})
    }
}
</script>

<style scoped>
    strong{color: green;}
</style>
//parent
<template>
  <div id="app">
    我是父组件,我收到子组件传来的数据为 <strong>{{y}}</strong>
    <Child :value="x" @sayHi="y = $event"/>
    <Sibling></Sibling>
  </div>
</template>

<script>
import Child from './components/Child'
import Sibling from './components/Sibling'
export default {data(){
    return {
      x: 'hi,child',
      y: ''
    }
  },
  components:{
    Child,
    Sibling
  }
}
</script>

<style scoped>
    strong{color: blue;}
</style>

对于 EventBus 这部分,可能存在这样一个疑难,既然 Vue 实例中都有 $emit$on,为什么不间接用 this.$emit 触发事件,this.$on 接管事件呢?还非得要额定一个空实例 eventBus = new Vue()。那是因为,Vue 中每个组件都是一个独自的 Vue 实例,你在这个 Vue 实例中触发该实例的 emit 事件,另外一个实例的 on 事件是接管不到的,不在一辆公交车上,怎么能进行事件通信呢?因而就必须要一个公共的公交车,也就是事件总线。

上述实例中的 eventBus 的应用办法是部分的 eventBus,谁要用到 eventBus 要本人手动引入。也能够将 eventBus 做成全局的,比方挂在 vue 的原型上。

//main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

Vue.prototype.$eventBus = new Vue();// 增加这句,肯定要在下方的 new Vue 前。new Vue({render: h => h(App),
}).$mount('#app')
//child
sibling(){this.$eventBus.$emit('sibling','hi,brother');
}
//sibling
mounted(){this.$eventBus.$on('sibling', (msg)=>{this.x = msg;})
}

除了上述的增加属性到 Vue 原型的形式外,还能够应用 Object.defineProperty() 为 Vue 原型增加属性。

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

let eventBus = new Vue()
Object.defineProperty(Vue.prototype,'$eventBus',{get(){return eventBus}
})

new Vue({render: h => h(App),
}).$mount('#app')

3.2 Vuex

参考博客

vue 篇之事件总线(EventBus)

退出移动版