Vue组件通信方式及其应用场景总结(下)

简介: 介绍 Vue 中的通信方式

二 vuex

vuex算是vue中处理复杂的组件通信的最佳方案,毕竟是vuevuex一个娘胎里出来的。而且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 支持异步组件通信

vuexactions允许我们做一些异步操作,然后通过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>

我们在初始化的时候通过EventBuson方法绑定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.0vue3.0源码,对3.0源码出了几篇文章,陆续会持续更新,感兴趣的同学可以直接点击阅读

vue3.0 响应式原理(超详细)

全面解析 vue3.0 diff算法

vue3.0 watch 和 computed源码解析(举例图解)

最后大家觉得还不错的话,就 点赞 + 关注 一波,持续分享技术文章。

公众号:前端Sharing

感谢观看🙏🙏🙏~~~

相关文章
|
3天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
3天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
3天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
3天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
2天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
2天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
|
17天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
4天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
4天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
9天前
|
JavaScript
Vue基础知识总结 4:vue组件化开发
Vue基础知识总结 4:vue组件化开发