一
二 vuex
vuex
算是vue
中处理复杂的组件通信的最佳方案,毕竟是vue
和vuex
一个娘胎里出来的。而且vuex
底层也是用vue
实现的。相信不少同学对vuex
并不陌生。接下来我们开始介绍vuex。
1 基础用法
vuex文件
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
state:{
fatherMes:'',
sonMes:'',
fatherMesAsync:''
},
mutations:{
sayFaher(state,value){
state.fatherMes = value
},
saySon(state,value){
state.sonMes = value
},
sayAsyncFather(state,value){
state.fatherMesAsync = value
}
},
actions:{
asyncSayFather({
commit },payload){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(payload)
},2000)
}).then(res=>{
commit('sayAsyncFather',res)
})
}
}
})
在store
文件中,我们声明三个mutations
分别是向父组件通信saySon
,父组件向子组件通信,同步方法sayFaher
和异步方法sayAsyncFather
,actions中模拟了一个三秒后执行的异步任务asyncSayFather
。
main.js
import store from './components/vuex/store'
new Vue({
render: h => h(App),
store
}).$mount('#app')
main.js
注入store
父组件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >同步:对子组件说</button><br/>
<input v-model="asyncMes" /> <button @click="asyncSend" >异步:对子组件说</button><br/>
<div>子组件对我说:{
{ sonMes }}</div>
<son />
</div>
</template>
<script>
import son from './son'
export default {
/* 父组件 */
name:'father',
components:{
son ,/* 子组件 */
},
data(){
return {
mes:'',
asyncMes:''
}
},
computed:{
sonMes(){
return this.$store.state.sonMes
}
},
mounted(){
console.log(this.$store)
},
methods:{
/* 触发mutations,传递数据给子组件 */
send(){
this.$store.commit('sayFaher',this.mes)
},
/* 触发actions,传递数据给子组件 */
asyncSend(){
this.$store.dispatch('asyncSayFather',this.asyncMes)
}
},
}
</script>
父组件分别触发同步异步方法,把信息发送给子组件。用computed
来接受vuex
中的state
。
子组件
<template>
<div class="son" >
<div> 父组件对我说:{
{ fatherMes }} </div>
<div> 父组件对我说(异步):{
{ fatherMesAsync }} </div>
<input v-model="mes" /> <button @click="send" >对父组件说</button>
</div>
</template>
<script>
export default {
name:'son',
data(){
return {
mes:'',
}
},
computed:{
/* 接受父组件同步消息 */
fatherMes(){
return this.$store.state.fatherMes
},
/* 接受父组件异步消息 */
fatherMesAsync(){
return this.$store.state.fatherMesAsync
}
},
methods:{
/* 向父组件发送信息 */
send(){
this.$store.commit('saySon',this.mes)
},
},
}
</script>
子组件的方法和父组件保持一致。
效果
2 优点
1 根本解决复杂组件的通信问题
vuex
在一定程度上根本解决了vue
复杂的组件通信情况,我们不再关心两个毫无干系的两个组件的通信问题。
2 支持异步组件通信
vuex
中actions
允许我们做一些异步操作,然后通过commit
可以把数据传入对应的mutation
,至于actions
为什么可以执行异步,是因为里面底层通过Promise.resolve
能够获取异步任务完成的状态。
3 缺点
1 流程相比稍微复杂
vuex
通信方式相比其他方式,比较复杂,而且如果不同的模块,需要建立独立的modules
。
4 应用场景
实际开发场景中,不会存在demo
项目这样简单的通信,vuex
的出现,就是解决这些比较复杂的组件通信场景。对于中大型项目,vuex
是很不错的状态管理,数据通信方案。
三 事件总线一 EventBus
EventBus
事件总线, EventBus
所有事件统一调度,有一个统一管理事件中心,一个组件绑定事件,另一个组件触发事件,所有的组件通信不再收到父子组件的限制,那个页面需要数据,就绑定事件,然后由数据提供者触发对应的事件来提供数据,这种通讯场景不仅仅应用在vue
,而且也应用在react
。
EventBus
核心思想是事件的绑定和触发,这一点和vue中 this.$emit` 和 `this.$on
一样,这个也是整个EventBus
核心思想。接下来我们来重点解析这个流程。
1 基本用法
EventBus
export default class EventBus {
es = {
}
/* 绑定事件 */
on(eventName, cb) {
if (!this.es[eventName]) {
this.es[eventName] = []
}
this.es[eventName].push({
cb
})
}
/* 触发事件 */
emit(eventName, ...params) {
const listeners = this.es[eventName] || []
let l = listeners.length
for (let i = 0; i < l; i++) {
const {
cb } = listeners[i]
cb.apply(this, params)
}
}
}
export default new EventBus()
这个就是一个简单的事件总线,有on
,emit
两个方法。
父组件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >对子组件说</button>
<div>子组件对我说:{
{ sonMes }}</div>
<son />
<brotherSon />
</div>
</template>
<script>
import son from './son'
import brotherSon from './brother'
import EventBus from './eventBus'
export default {
name:'father',
components:{
son ,/* 子组件 */
brotherSon, /* 子组件 */
},
data(){
return {
mes:'',
sonMes:''/* 发送给子组件的信息 */
}
},
mounted(){
/* 绑定事件 */
EventBus.on('sonSay',this.sonSay)
},
methods:{
/* 传递给子组件 */
send(){
EventBus.emit('fatherSay',this.mes)
},
/* 接受子组件信息 */
sonSay(value){
this.sonMes = value
},
},
}
</script>
我们在初始化的时候通过EventBus
的on
方法绑定sonSay
方法供给给子组件使用。向子组件传递信息的时候,通过emit
触发子组件的绑定方法,实现了父子通信。
接下来我们看一下子组件。
子组件
<template>
<div class="son" >
<div> 父组件对我说:{
{ fatherMes }} </div>
<input v-model="mes" /> <button @click="send" >对父组件说</button>
<div>
<input v-model="brotherMes" /> <button @click="sendBrother" >对兄弟组件说</button>
</div>
</div>
</template>
<script>
import EventBus from './eventBus'
export default {
name:'son',
data(){
return {
mes:'',
brotherMes:'',
fatherMes:''
}
},
mounted(){
/* 绑定事件 */
EventBus.on('fatherSay',this.fatherSay)
},
methods:{
/* 向父组件传递信息 */
send(){
EventBus.emit('sonSay',this.mes)
},
/* 向兄弟组件传递信息 */
sendBrother(){
EventBus.emit('brotherSay',this.brotherMes)
},
/* 父组件对我说 */
fatherSay(value){
this.fatherMes = value
}
},
}
</script>
和父组件的逻辑差不多,把需要接受数据的方法,通过EventBus
绑定,通过触发eventBus方法,来向外部传递信息。我们还模拟了兄弟之间通信的场景。我们建立一个兄弟组件。
<template>
<div class="son" > 兄弟组件对我说: {
{ brotherMes }} </div>
</template>
<script>
import EventBus from './eventBus'
export default {
/* */
name:'brother',
data(){
return {
brotherMes:''
}
},
mounted(){
/* 绑定事件给兄弟组件 */
EventBus.on('brotherSay',this.brotherSay)
},
methods:{
brotherSay(value){
this.brotherMes = value
}
}
}
</script>
我们可以看到,兄弟组件处理逻辑和父子之间没什么区别。
效果
2 优点
1 简单灵活,父子兄弟通信不受限制。
eventBus的通信方式,相比之前的几种比较简单,而且不受到组件层级的影响,可以实现任意两个组件的通信。需要数据就通过on
绑定,传递数据就emit
触发。
2 通信方式不受框架影响
eventBus的通信方式,不只是vue可以用,react,小程序都可以使用这种通信方式,而且笔者感觉这种通信方式更适合小程序通信,至于为什么稍后会一一道来。
4 缺点
1 维护困难,容易引起连锁问题
如果我们采用事件总线这种通信模式,因为所有事件是高度集中,统一管理的,中间如果有一个环节出现错误,就会造成牵一发动全身的灾难.而且后期维护也是十分困难的。
2 需要谨小慎微的命令规范
现实的应用场景,要比demo场景复杂的多,实际场景会有无数对父子组件,无数对兄弟组件,我们不肯能每个事件都叫相同名字,所以eventBus
绑定事件的命名要有严格的规范,不能起重复名字,也不能用错名字。
3 不利于组件化开发
eventBus
通信方式是无法进行有效的组件化开发的,假设一个场景,一个页面上有多个公共组件,我们只要向其中的一个传递数据,但是每个公共组件都绑定了数据接受的方法。我们怎么样做到把数据传递给需要的组件呢?
4 应用场景
实现总线这种方式更适合,微信小程序,和基于vue构建的小程序,至于为什么呢,因为我们都知道小程序采用双线程模型(渲染层+逻辑层)(如下图所示),渲染层作用就是小程序wxml
渲染到我们的视线中,而逻辑层就是我们写的代码逻辑,在性能上,我们要知道在渲染层浪费的性能要远大于逻辑层的代码执行性能开销,如果我们在小程序里采用通过props
等传递方式,属性是绑定在小程序标签里面的,所以势必要重新渲染视图层。如果页面结构复杂,可能会造成卡顿等情况,所以我们通过eventBus
可以绕过渲染层,直接有逻辑层讲数据进行推送,节约了性能的开销。
四 事件总线二 new Vue
new Vue
这种通信方式和eventBus
大致差不多,有一点不同的是,以vue
实例作为eventBus
中心,除了我们可以用$on`,`$emit
之外,我们还可以用vue下的data
,watch
等方法,而且我们建立多个多个vue
,作为不同模块的数据通信桥梁,相比上边那个EventBus
方法,new Vue
这种方法更高效,更适合vue
项目场景。我们接着往下看。
1 基本使用
VueBus
import Vue from 'vue'
export default new Vue()
父组件
<template>
<div class="father" >
<input v-model="mes" /> <button @click="send" >对子组件说</button>
<div>子组件对我说:{
{ sonMes }}</div>
<son />
</div>
</template>
<script>
import son from './son'
import VueBus from './vueBus'
export default {
/* 父组件 */
name:'father',
components:{
son ,/* 子组件 */
},
data(){
return {
mes:'',
sonMes:'' /* 发送给子组件的信息 */
}
},
created(){
/* 绑定属性 */
VueBus._data.mes = 'hello,world'
},
mounted(){
/* 绑定事件 */
VueBus.$on('sonSay',this.sonSay)
},
methods:{
/* 传递给子组件 */
send(){
VueBus.$emit('fatherSay',this.mes)
},
/* 接受子组件信息 */
sonSay(value){
this.sonMes = value
},
},
}
</script>
我们通过 $on
绑定了接受数据的方法,初始化的时候向 vue_data下面绑定了数据。
子组件
<template>
<div class="son" >
<div> 父组件对我说:{
{ fatherMes }} </div>
<input v-model="mes" /> <button @click="send" >对父组件说</button><br/>
<button @click="getFatherMes" >获取数据</button>
</div>
</template>
<script>
import VueBus from './vueBus'
export default {
name:'son',
data(){
return {
mes:'',
brotherMes:'',
fatherMes:''
}
},
mounted(){
/* 绑定事件 */
VueBus.$on('fatherSay',this.fatherSay)
},
methods:{
/* 向父组件传递信息 */
send(){
VueBus.$emit('sonSay',this.mes)
},
/* 父组件对我说 */
fatherSay(value){
this.fatherMes = value
},
/* 获取父组件存入vue中的数据 */
getFatherMes(){
console.log( VueBus._data.mes )
}
},
}
</script>
和eventBus
事件总线一样,我们还可以直接通过_data
数据直接获取到父组件传递的内容。
效果
2 优点
1 简单灵活,任意组件之间通信。
和上边eventBus
通信方式一样,这种通信方式很灵活,可以轻松在任意组件间实现通信。
2 除了通信还可以使用watch
, computed
等方法
如果我们通过vue作为通信媒介,那么只用其中的$emit`和`$on
真的是有点大材小用了,既然实例了一个vue
,我们可以轻松的使用vue的 $watch
computed
等功能。
3 缺点
基本上EventBus
的缺点,都在vue
这种通信方式中都有存在。
4 应用场景
在项目中不考虑用vuex
的中小型项目中,可以考虑采用vue
事件总线这种通信方式,在使用的这种方式的时候,我们一定要注意命名空间,不要重复绑定事件名称。分清楚业务模块,避免后续维护困难。
写在后面
我们在写vue
项目中,具体要用什么通信方式,还要看具体的业务场景,项目大小等因素综合评估。文章中给大家介绍了vue通信方式的优缺点,可以给大家实际工作提供一个参考。
笔者在工作之余一直在看vue2.0
和vue3.0
源码,对3.0源码出了几篇文章,陆续会持续更新,感兴趣的同学可以直接点击阅读
vue3.0 watch 和 computed源码解析(举例图解)
最后大家觉得还不错的话,就 点赞 + 关注 一波,持续分享技术文章。
公众号:前端Sharing
感谢观看🙏🙏🙏~~~