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

感谢观看🙏🙏🙏~~~

相关文章
|
25天前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
27天前
|
JavaScript 前端开发 开发者
vue 数据驱动视图
总之,Vue 数据驱动视图是一种先进的理念和技术,它为前端开发带来了巨大的便利和优势。通过理解和应用这一特性,开发者能够构建出更加动态、高效、用户体验良好的前端应用。在不断发展的前端领域中,数据驱动视图将继续发挥重要作用,推动着应用界面的不断创新和进化。
|
21天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
126 64
|
22小时前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
28天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
32 1
vue学习第一章
|
28天前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
26 1
vue学习第三章
|
28天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
35 1
vue学习第四章
|
28天前
|
JavaScript 前端开发 算法
vue学习第7章(循环)
欢迎来到瑞雨溪的博客,一名热爱JavaScript和Vue的大一学生。本文介绍了Vue中的v-for指令,包括遍历数组和对象、使用key以及数组的响应式方法等内容,并附有综合练习实例。关注我,将持续更新更多优质文章!🎉🎉🎉
24 1
vue学习第7章(循环)
|
21天前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
28 8
|
21天前
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。