Vue2组件通信方式及其应用场景

写在最前:文章转自掘金

一、prop & this.emit('Method name', value)

1. 优点

父子组件通信方面灵活

2. 缺点

  • props 对父组件数据的篡改
  • 跨层级通信,兄弟组件通讯困难

3. 应用场景

props的应用场景很简单,就是正常的父子组件通信

二、this.$xxx

实际操作中会有很大的弊端,而且vue本身也不提倡这种通信方式。而且这种通信方式也有很多风险性。

三、provide inject

1. 基本用法

在父组件上通过provide将方法,属性,或者是自身实例暴露出去,子孙组件,插槽组件,甚至是子孙组件的插槽组件,通过inject把父辈provide引进来。
父组件:

<template>
  <div class="father" >
     <div>子组件对我说:{{  sonMes  }}</div>
     <div>孙组件对我说:{{  grandSonMes  }}</div>
     <son />
  </div>
</template>
<script>
import son from './son'
export default {
   name:'father',
   components:{
       son /* 子组件 */
   },
   provide(){
       return {
           /* 将自己暴露给子孙组件 ,这里声明的名称要于子组件引进的名称保持一致 */
           father:this
       }
   },
   data(){
       return {
          grandSonMes:'', /* 来自子组件的信息 */
          sonMes:''      /* 发送给子组件的信息  */
       } 
   },
   methods:{
      /* 接受孙组件信息 */
      grandSonSay(value){
          this.grandSonMes = value
      },
      /* 接受子组件信息 */ 
      sonSay(value){
          this.sonMes = value
      },
   },
}
</script>

这里我们通过provide把本身暴露出去。⚠️⚠️⚠️这里声明的名称要与子组件引进的名称保持一致
子组件

<template>
    <div class="son" >
        <input  v-model="mes"   /> <button @click="send"  >对父组件说</button>
        <grandSon />
    </div> 
</template>

<script>
import  grandSon from './grandSon'
export default {
    /* 子组件 */
   name:'son',
   components:{
       grandSon /* 孙组件 */
   },
   data(){
       return {
           mes:''
       }
   },
   /* 引入父组件 */
   inject:['father'],
   methods:{
       send(){
           this.father.sonSay(this.mes)
       }
   },
    
}
</script>

子组件通过inject把父组件实例引进来,然后可以直接通过this.father可以直接获取到父组件,并调用下面的sonSay方法。
孙组件

<template>
   <div class="grandSon" >
        <input  v-model="mes"  /> <button @click="send"  >对爷爷组件说</button>
    </div> 
</template>

<script>
export default {
    /* 孙组件 */
   name:'grandSon',
   /* 引入爷爷组件 */
   inject:['father'],
   data(){
       return {
           mes:''
       }
   },
   methods:{
       send(){
           this.father.grandSonSay( this.mes )
       }
   }
}
</script>

2. 插槽方式

provide , inject 同样可以应用在插槽上,我们给父子组件稍微变动一下。
父组件

<template>
  <div class="father" >
     <div>子组件对我说:{{  sonMes  }}</div>
     <div>孙组件对我说:{{  grandSonMes  }}</div>
     <son >
         <grandSon/>
     </son>
  </div>
</template>
<script>
import son from './slotSon'

import grandSon from './grandSon' 
export default {
   name:'father',
   components:{
       son, /* 子组件 */
       grandSon /* 孙组件 */
   },
   provide(){
       return {
           /* 将自己暴露给子孙组件 */
           father:this
       }
   },
   data(){
       return {
          grandSonMes:'', /* 来自子组件的信息 */
          sonMes:''      /* 发送给子组件的信息  */
       } 
   },
   methods:{
      /* 接受孙组件信息 */
      grandSonSay(value){
          this.grandSonMes = value
      },
      /* 接受子组件信息 */ 
      sonSay(value){
          this.sonMes = value
      },
   },
}
</script>

子组件

<template>
    <div class="son" >
        <input  v-model="mes"   /> <button @click="send"  >对父组件说</button>
        <slot />
    </div> 
</template>

达到了同样的通信效果。实际这种插槽模式,所在都在父组件注册的组件,最后孙组件也会绑定到子组件的children下面。和上述的情况差不多。

3. provied其他用法

provide不仅能把整个父组件全部暴露出去,也能根据需要只暴露一部分(一些父组件的属性或者是父组件的方法),上述的例子中,在子孙组件中,只用到了父组件的方法,所以我们可以只提供两个通信方法。但是这里注意的是,如果我们向外提供了方法,如果方法里面有操作this行为,需要绑定this
父组件

   provide(){
       return {
           /* 将通信方法暴露给子孙组件(注意绑定this) */
           grandSonSay:this.grandSonSay.bind(this),
           sonSay:this.sonSay.bind(this)
       }
   },   
   methods:{
      /* 接受孙组件信息 */
      grandSonSay(value){
          this.grandSonMes = value
      },
      /* 接受子组件信息 */ 
      sonSay(value){
          this.sonMes = value
      },
   },

子组件

/* 引入父组件方法 */
   inject:['sonSay'],
   methods:{
       send(){
           this.sonSay(this.mes)
       }
   },

4. 优缺点

  • 组件通信不受到子组件层级的影响
  • 适用于插槽,嵌套插槽

  • 不适合兄弟通讯
  • 父级组件无法主动通信

5. 应用场景

provide-inject这种通信方式,更适合深层次的复杂的父子代通信,子孙组件可以共享父组件的状态,还有一点就是适合el-form el-form-item这种插槽类型的情景。

四、vuex

五、事件总线一 EventBus

EventBus事件总线, EventBus 所有事件统一调度,有一个统一管理事件中心,一个组件绑定事件,另一个组件触发事件,所有的组件通信不再收到父子组件的限制,那个页面需要数据,就绑定事件,然后由数据提供者触发对应的事件来提供数据。

EventBus 核心思想是事件的绑定和触发,这一点和vuethis.$emitthis.$on一样,这个也是整个EventBus核心思想。接下来我们来重点解析这个流程。

1. 基本用法

EventBus

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>

我们可以看到,兄弟组件处理逻辑和父子之间没什么区别。


效果图.gif

2. 优缺点

  • 简单灵活,父子兄弟通信不受限制。
  • 通信方式不受框架影响
  • 维护困难,容易引起连锁问题
  • 需要谨小慎微的命令规范
  • 不利于组件化开发

3. 应用场景

实现总线这种方式更适合,微信小程序,和基于vue构建的小程序,至于为什么呢,因为我们都知道小程序采用双线程模型(渲染层+逻辑层)(如下图所示),渲染层作用就是小程序wxml渲染到我们的视线中,而逻辑层就是我们写的代码逻辑,在性能上,我们要知道在渲染层浪费的性能要远大于逻辑层的代码执行性能开销,如果我们在小程序里采用通过props等传递方式,属性是绑定在小程序标签里面的,所以势必要重新渲染视图层。如果页面结构复杂,可能会造成卡顿等情况,所以我们通过eventBus可以绕过渲染层,直接有逻辑层讲数据进行推送,节约了性能的开销。

wallhaven-135750.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,431评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,637评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,555评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,900评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,629评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,976评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,976评论 3 448
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,139评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,686评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,411评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,641评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,129评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,820评论 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,233评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,567评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,362评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,604评论 2 380

推荐阅读更多精彩内容