我们知道,vue中的一个大页面是由多个子组件组成,组件之间的关系往往错综复杂,因此我们了解组件的通信方式成为vue中的重中之重。这篇文章就来总结一下vue项目中的几种通信方式。
一. 父子组件通信
1.props/$emit
父组件通过props向子组件传递数据,子组件通过$emit触发父组件的方法,从而实现子到父的通信
<!--父组件-->
<template>
<div class="parent">
<!-- 通过v-bind或 ":" 父组件向子组件传递value-->
<!-- 子组件中通过$emit触发, 父组件中通过v-on 或 "@" 接受emit的方法,进而实现子向父数据传递 -->
<child :myValue="value" @changeValue="changeValue"></child>
</div>
</template>
<script>
import Child from './Child'
export default {
name: 'Parent',
components: { Child },
data() {
return {
value: '我要传给子组件'
}
},
methods:{
changeValue(changedValue){
this.value = changedValue
}
}
}
</script>
// 子组件Child.vue
<template>
<div class="child" @click="emitParent">
{{myValue}}
</div>
</template>
<script>
import Child from './Child'
export default {
name: 'Child',
props: { // 子组件通过props接受
myValue: String,
},
data() {
return {
}
},
methods:{
emitParent(){
// 子组件中通过$emit触发, 父组件中通过v-on 或 "@" 接受emit的方法,进而实现子向父数据传递
this.$emit('changeValue', this.myValue + 1)
}
}
}
</script>
2.$parent/$children(不推荐使用)
通过this.$parent可以拿到当前组件的父组件(如果有的话),通过this.$children可以拿到它的所有子组件(如果有的话),因为this.$children是一个数组,如果要拿到它特定的子组件,需要知道对应子组件所在的位置的下标。
没有特殊情况一般不必使用,这里仅作了解。
二. 非父子组件通信
1.provide/inject
provide/inject是vue 2.2.0 新出的api,主要用于父、子、孙及所有后代之间的通信。通过provide属性在父组件中提供指定属性,在需要的子孙组件(不论组件嵌套有多深)中通过inject注入所需属性。
举个例子
// Father.vue
<template>
<div>
<child></child>
</div>
</template>
<script>
import Child from '../components/Child.vue'
export default {
name: 'Father',
provide: {
for: 'demo'
},
components: {
Child
}
}
</script>
// Child.vue
<template>
<div>
{{for}}
</div>
</template>
<script>
export default {
name: 'Child',
inject: ['for'],
}
</script>
2.ref/refs
ref: 如果在普通DOM元素上使用,则指向dom元素本身。如果用在组件元素上,则指向这个组件的实例。可以通过this.$refs.ref属性名使用组件的data、methods等。
使用场景:一般可用于一些弹窗的显示和隐藏。
// 子组件 A.vue
<script>
export default {
data () {
return {
name: 'Vue.js'
}
},
methods: {
sayHello () {
console.log('hello')
}
}
}
</script>
// 父组件 app.vue
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.name); // Vue.js
comA.sayHello(); // hello
}
}
</script>
3.eventBus
eventBus俗称叫事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
主要分为以下四步
- 初始化eventBus,简单来说就是执行new Vue()
- 在所需要的组件中导入eventBus,通过eventBus.$emit() 触发事件。
- 在需要接受事件的组件中通过eventBus.$on()接受事件
- eventBus.$off()来注销事件
// 创建event-bus
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 在所需组件中$emit()触发事件
// addtionNum.vue 中触发事件
<template>
<div>
<button @click="additionHandle">+加法器</button>
</div>
</template>
<script>
import {EventBus} from './event-bus.js'
console.log(EventBus)
export default {
data(){
return{
num:1
}
},
methods:{
additionHandle(){
EventBus.$emit('addition', {
num:this.num++
})
}
}
}
</script>
// 通过$on接收事件
// showNum.vue 中接收事件
<template>
<div>计算和: {{count}}</div>
</template>
<script>
import { EventBus } from './event-bus.js'
export default {
data() {
return {
count: 0
}
},
mounted() {
EventBus.$on('addition', param => {
this.count = this.count + param.num;
})
}
}
</script>
// 通过$off移除事件
import { eventBus } from 'event-bus.js'
EventBus.$off('addition', {})
eventBus一般用于逻辑比较简单的系统,逻辑过于复杂,eventBus的维护会十分困难。这时候一般推荐使用vuex代替eventBus.
4.vuex
vuex是一种状态管理模式,它保存着组件的公用的状态,并且以相应的规则保证状态变化。vuex的核心就是一个store,它相当于是一个容器,里面包含有state,action,mutation,getter,modules等核心
- state:用于数据的存储,是store中的唯一数据源
- getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
- mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
- actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
- modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护
以下是一个简单的vuex实例
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
//可以通过以下方式来读取state
console.log(store.state.count)
store.commit("commit")