Vue中如何实现兄弟组件之间的通讯?

1. 前言

在Vue开发中,组件是数据的展现和交互的封装。组件之间的通讯是Vue框架中最常见的问题之一,本文将会介绍如何在Vue中实现兄弟组件之间的通讯。

2. 父组件向子组件传递数据

2.1 props

Vue使用props属性来从父组件向子组件传递数据。子组件通过props属性定义接收的属性并通过this来访问传递过来的值。

//父组件

<template>

<div>

<child :message="parentMsg"></child>

</div>

</template>

<script>

import Child from './Child';

export default {

name: 'parent',

data() {

return {

parentMsg: '这是来自父组件的消息'

}

},

components: {

Child

}

}

</script>

//子组件

<template>

<div>

{{ message }}

</div>

</template>

<script>

export default {

name: 'child',

props: ['message']

}

</script>

props使用方式:

在父组件中用冒号(:)绑定数据到子组件的props中。

在子组件中使用props选项并指定接收的属性名称。

2.2 动态props

props属性支持使用js表达式,可以将props属性和父组件的数据动态绑定。Vue对props的改变不会影响父组件的状态,但是就算是引用类型也不会影响引用数据本身,在子组件中对引用类型的数据的修改也不会被父组件观察到。

//父组件

<template>

<div>

<child :message="parentMsg" :count="parentCount"></child>

</div>

</template>

<script>

import Child from './Child';

export default {

name: 'parent',

data() {

return {

parentMsg: '这是来自父组件的消息',

parentCount: 1

}

},

components: {

Child

}

}

</script>

//子组件

<template>

<div>

{{ message }} {{ count }}

</div>

</template>

<script>

export default {

name: 'child',

props: ['message', 'count']

}

</script>

3. 子组件向父组件传递数据

3.1 $emit

$emit触发一个父元素事件,并传递任意数据,父元素可以通过$on监听子元素事件并接收数据。

//父组件

<template>

<div>

<child @son-click="listenClick"></child>

</div>

</template>

<script>

import Child from './Child.vue';

export default {

name: 'parent',

methods: {

listenClick(res) {

console.log(res);

}

},

components: {

Child

}

}

</script>

//子组件

<template>

<div>

<button @click="handleClick">点击我</button>

</div>

</template>

<script>

export default {

name: 'child',

methods: {

handleClick() {

this.$emit('son-click', '这是来自子组件的数据');

}

}

}

</script>

$emit使用方式:

在子组件中使用this.$emit触发事件并传递数据

在父组件中使用@监听子组件事件并接收数据

4. 兄弟组件之间的通讯

4.1 $parent

可以使用$parent来在子组件中访问父组件的数据和方法,此方法不够优雅,父子级关系发生变化时会导致错误。

//组件A

<template>

<div>

<button @click="$parent.$emit('change', '我変な数据')">我变!</button>

</div>

</template>

//组件B

<template>

<div>

{{ msg }}

</div>

</template>

<script>

export default {

name: 'componentB',

data() {

return {

msg: ''

};

},

mounted() {

const that = this;

this.$parent.$on('change', function(res){

that.msg = res;

});

}

}

</script>

$parent缺点:

不能解决子组件之间通讯的问题

如果你的组件嵌套比较多,又多处需要调用$pantom值,那么显得比较混乱

如果组件结构调整就会导致产生异常

4.2 $refs

可以使用refs来获取子组件的引用,并直接访问子组件的 props 和 data 属性。

//组件A

<template>

<div>

<button @click="$refs.B.changeData('我变の数据')">我变!</button>

</div>

</template>

//组件B

<template>

<div>

{{ msg }}

</div>

</template>

<script>

export default {

name: 'componentB',

data() {

return {

msg: ''

};

},

methods: {

changeData(str) {

this.msg = str;

}

}

}

</script>

$refs优点:

适用于那些只在单个页面中复用的非常简单的组件,通常只是表单控件或一个按钮之类的。

浅组合的情况下谨慎使用,以防改变代码可能会很脆弱。

4.3 Event Bus

Event Bus 是一种Vue的高级技巧。这个技巧的基础是Vue实例可以作为中央事件总线:允许在任何组件上触发事件和监听事件。

//event-bus.js

import Vue from 'vue';

const bus = new Vue();

export default bus;

//组件A

<template>

<div>

<button @click="changeData('我变! 的数据')">我变!</button>

</div>

</template>

<script>

import bus from '../event-bus';

export default {

name: 'componentA',

methods: {

changeData(str) {

bus.$emit('change', str);

}

}

}

</script>

//组件B

<template>

<div>

{{ msg }}

</div>

</template>

<script>

import bus from '../event-bus';

export default {

name: 'componentB',

data() {

return {

msg: ''

};

},

created() {

const that = this;

bus.$on('change',function(res){

that.msg = res;

});

}

}

</script>

Event Bus优点:

非常适合大型的复杂应用程序,它需要进行跨多个组件通信(例如公共App组件等)。

最佳实践是在单独的event-bus.ts或event-bus.js小模块中导出Event Bus。

4.4 Vuex

在组件树中共享状态非常复杂并且极难维护,这是Vuex背后的基础思想,也是Vuex所解决的核心问题。

//store.js

import Vue from 'vue';

import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({

state: {

msg: '初始的数据'

},

mutations: {

change(state, value) {

state.msg = value;

}

},

actions: {

}

});

export default store;

//组件A

<template>

<div>

<button @click="changeData('我变の数据')">我变!</button>

</div>

</template>

<script>

import { mapMutations } from 'vuex';

export default {

name: 'componentA',

methods: {

...mapMutations(['change']),

changeData(str) {

this.change(str);

}

}

}

</script>

//组件B

<template>

<div>

{{ msg }}

</div>

</template>

<script>

import { mapState } from 'vuex';

export default {

name: 'componentB',

computed: {

...mapState(['msg'])

}

}

</script>

Vuex优点:

Vuex广泛应用于大型的复杂应用程序中。我们可以在其中使用属性、getters、mutators、操作和订阅状态等技术。

Vuex-store很少变化(不能使用this),当发生变化时,容易跟踪。但是组件会发生变化,由于一些bug,很难明确跟踪它们所绑定的值何时变化。此外,代码相对稳定的store意味着测试更加容易。

5. 总结

组件是Vue中数据展现和交互的封装,组件之间的通讯使用props、$emit、$parent、$refs、Event Bus和Vuex。在选择通讯方式时,需要据其优缺点和应用场景进行选择。