前言

最近事情比较多,已经一个月没有更新博客了,想想还是不能停下前进的脚步,今天抽空继续记录下本系列的内容,上篇文章说了组件开发,那么本篇就说说组件间通信的那些事吧!

通信模式

我们都知道,Vue 的数据模型为单向数据流+事件通知,其实组件间通信也是一样,大部分时候常用的也都是用 prop 事件向下传递数据,然后通过事件通知向上通知数据变化。或者使用 $refs 直接引用组件实例,调用组件方法,实现数据传递。除此之外,还可以使用 vuex 来统一管理数据及状态,然后通过调用 vuex 中的 MutationAction 来改变 state 中的数据。再或者使用 EventBus 来进行一个全局的事件通知改变数据。

prop + 事件通知

在 Vue 中,组件间通常通过 prop 来进行想下的数据传递,如:

1
2
3
4
<form-box
:value="val"
:data="form"
/>

既在子组件中定义 prop 属性,然后在父组件中将数据通过属性绑定的方式传递给子组件,这就实现了数据的自上像下传递,那么在子组件中就可以接受到来自父组件的数据,但是,如果某个数据我们想要从子组件传递给父组件要怎么处理了?通常使用事件通知的方式,既在父组件中绑定某个事件,当子组件内的某些数据发生变化的时候,可以通过 $emit 方法触发该事件,并将数据作为参数传递给父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- 父组件中 -->
<form-box
:value="val"
:data="form"
@submit="submitForm"
/>

<script>
export default {
motheds: {
submitForm(form) {
// do some thing
},
},
};
</script>


<!-- 子组件 -->
<script>
export default {
motheds: {
submit() {
// 触发submitForm事件,将data数据传递给父级
this.$emit('submitForm', this.data);
},
},
};
</script>

这就完成了一个简单的数据循环,该方式作为官方推荐的方式,我们很容易就看出改方式确实是简单实用,但是,它的缺点也很明显。在日常工作中,我们经常会遇到很多复杂的场景,还遇到很多类似组件嵌套结构比较深的场景,既一个父组件会包含很多层子孙级组件,那么在该中场景下,在使用该模式,很显然就有点麻烦,我们需要将数据一层层的通过 prop 向下传递,这相对还比较容易,但是当我们从最底层组件中,想要将数据传递给最上层组件时,就不得不在每层组件中都定义相似的方法,然后不断的向上触发事件,这显然有点不太合适,这就使得另一种方式的出现!

($refs or $parent or $root) + 实例方法调用

除了上面说的使用 props 传递数据以外,我们也可以使用 $refs or $parent or $root 引用组件实例,然后通过实例,直接调用实例方法,并且将要传递的数据通过参数进行传递:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!-- 父组件中 -->
<form-box
ref="formBox"
:value="val"
:data="form"
@submit="submitForm"
/>

<script>
export default {
motheds: {
submitForm(form) {
// do some thing
},
},
mounted() {
this.$refs.formBox.setValue('xxx');
},
};
</script>


<!-- 子组件 -->
<script>
export default {
motheds: {
setValue(val) {
this.value = val;
},
submit() {
// 调用父组件submitForm事件,将data数据传递给父级
this.$parent.submitForm('xxx');
},
},
};
</script>

通过上面的例子我们能很清楚的看出这种方式的使用方式,但是这里需要说明的是,这种方式会导致组件间的强耦合,并不推荐使用!

vuex方式

除了上面说的方法以外,在一些复杂场景中,我们更多的会使用 vuex 来统一进行数据管理,这有个好处就是它能很容易的处理夸层级的组件间的数据通信,既统一在 vuexstate 中定义数据,然后在 vuexMutation 中定义同步方法用来改变 state 中的数据,在 Action 中定义异步方法,通过触发 Mutation 或者直接改变 state 数据,从而达到全局数据通信的目的!

common vuex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// common vuex

const state = {
form: {
age: '',
name: '',
address: ''
},
};

const mutations = {
setFormData({ state }, data) {
this.state = JSON.parse(JSON.stringify(data || {}));
}
};

const actions = {
getFormData({ commit }) {
ajax.get('http://xxx.com/xxx', (res) => {
if (res.code === 200) {
commit('setFormData', res.data);
}
})
}
}

最外层组件:

1
2
3
4
5
6
7
8
9
10
11
<script>
import { mapState } from 'vuex';

export default {
computed: {
...mapState({
form: state => state.common.form,
}),
}
}
</script>

内层组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
export default {
data() {
return {
form: {
name: '',
age: '',
address: '',
},
};
},
methods: {
submit() {
this.$store.commit('setFormData', { ...this.form });
}
}
}
</script>

如上面 vuex 的代码,我们定义了 state 中的数据 form,当在form表单组件中,我们改变了数据之后,通过调用 Mutation 里的 setFormData 方法重新设置 state 中的数据 form,这样只需要在外层组件中绑定 state 中的数据 form,就可以实时拿到最新的 form 数据了,这样也就实现了夸组件间的数据通信!

EventBus方式

除了上面说的两种方式以外,还有一种情况我们会经常遇到,比如最内层组件并不依赖外层组件的数据,但是当最内层数据变化时,又需要通知到最外层组件,这个时候我们就可以使用 EventBus 方式。

其实作为一个前端,对于自定义事件的订阅/发布模式肯定是不陌生的,EventBus说白了,就是一个全局的事件订阅/发布管理器,我们通过全局订阅事件,并且在合适的时候触发事件,即可实现跨组件数据通信,关于 EventBus 的具体实现,可以参考我以前写的一个项目里的事件订阅/发布模块:事件订阅发布;这里就不在重复实现相关模块了!

具体使用中,我们只需要实现一个事件订阅/发布管理模块,并且进行一下简单的封装,将其封装成 vue 插件,然后再使用 Vue.use 方法注册一下即可,具体 vue 插件的封装,就不在本篇来讲了,以后会有一篇文章来专门说明 vue 插件开发,现在我们就当该插件已经实现,我们来看一下使用方法;

首先在main.js里面进行全局注册:

1
2
3
4
import Vue from 'vue';
import EventBus from '@/utils/event-bus';

Vue.use(EventBus);

在外层组件内进行事件订阅:

1
2
3
4
5
6
7
8
9
<script>
export default {
mounted() {
this.$bus.$on('submit-form', (data) => {
// do some thing
});
},
};
</script>

在内层组件中触发事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
export default {
data() {
return {
form: {
name: '',
age: '',
address: '',
},
};
},
methods: {
submit() {
this.$bus.$emit('submit-form', { ...this.form });
},
},
};
</script>

这样我们就通过全局事件订阅/发布模式实现了跨组件间的通信。

结尾

到这里,我们已经介绍了常用的4种组件间通信的方式,这几种方式各有利弊,我们可以根据具体的业务场景来选择使用哪种方式比较合理!

转载说明

本文允许全文转载,转载请注明来源:
平凡公子 - vue从入门到精通入门篇之组件间通信