《Vue》组件各种通讯方式的使用场景

Vue 封装得很好,有很多API供我们选择,导致我这种选择困难户老纠结用哪种。。。

这次就来梳理一下Vue组件的通讯方式的使用场景

1. props

这个不需要多说了吧。爸爸和儿子组件最直接的通讯方式!
总结就是:爸爸在儿子身上绑定属性,儿子通过props接收,再通过$emit反馈给爸爸

// 爸爸
<Son word="好好做人"  @msg="sonSay"/>
methods: {
  sonSay(event) {
    console.log(event); // 我不听
  }


// 儿子
props: ['word']
methods: {
  fn() {
     console.log(this.word); // 好好做人
     this.$emit(msg, '我不听');
  }
} 

优点:

props是使用频率最多的一种通讯方式,因为它导致的整个事件触发的过程可以追溯(你可以通过Vue DevTools浏览器插件看到)。一切变化都明明白白!

缺点:

爷爷和孙子交流?哥哥和弟弟交流?弟弟和表哥的朋友的女朋友交流?
这些场景,用props不是说不行,就是一层一层传递比较麻烦。。。

使用场景:

爸爸组件和儿子组件的通信,首选!不嫌麻烦,爷爷和孙子通信也可以。
但是如果关系比较疏远,就别用了,很费劲。。


2. $attrs 和 $listeners

一句话总结:$attrs 是能接收 非props 属性;而$listeners可以调用爸爸或者祖先的 methods

举个栗子:

组件父子情况:CompA --> CompB --> CompC --> CompD

// CompA
<div class="comp-a">
  <!-- 将这两个东西传下去 -->
  <CompB :num="num" @comp="handleComp"></CompB>
</div>
<script>
data: () => ({
  num: 'aaa',
}),
methods: {
  handleComp(newNum) {
    console.log('hello, handleComp~~~', newNum) // 被CompD点击修改成 {num: "改成DDD"}
    // 然后可以赋值给 num
    this.num = newNum;
  },
},
</script>
// CompB
  <div class="comp-b">
    <CompC v-bind="$attrs" v-on="$listeners"></CompC>
  </div>
// CompC
  <div class="comp-c">
    <CompD v-bind="$attrs" v-on="$listeners"></CompD>
  </div>
// CompD
<div class="comp-d">
  <button @click="handleClick">DDD</button>
</div>
<script>
 export default {
  methods: {
    handleClick() {
      console.log('CompD click...')
      console.log(this.$listeners)
      this.$listeners.comp('改成DDD');  // 调用一层层传下来的 comp 函数
    },
  },
  created(){
    console.log(this.$attrs); // {num: "aaa"}
  }
}
</script>

CompA中通过v-bind="$attrs"一层一层的将num属性传递给CompD

然后通过v-on="$listeners一层一层的将handleComp方法传递给CompD

CompD通过this.$listeners.comp直接调用CompA的方法,从而修改num属性

通过$attrs处理过的组件,属性会附着在该组件的根元素上

CompD.png

其实个人觉得这种作风不太像Vue,Vue之前一般是发出一个通知 (props和$emit的处理方法),交给上级去处理。
反而有点像 React 的处理方法,通过调用上级的方法去修改属性。

但是它代码确实没有props&$emit臃肿,但是原理还是一层一层传递。
可以通过设置属性 inheritAttrs: false,去掉

优缺点:

和第一种props差不多,比第一种好就好在如果跨越组件很多层,传递稍微方便。
但是兄弟通讯还是不方便。

使用场景:

组件层次比较深,且组件都是“独生子”。


3. provide 和 inject

这种方法就相当于:发广播,接收广播
provide向后代们发出广播,后代有需要的话,就可以接收广播(不需要就不接)

举个栗子:

组件父子情况:CompA --> CompB --> CompC --> CompD

// CompA.vue
<script>
data() => ({
  hobbit: '打篮球',
}),
provide() {
    return {
        CompAHobbit: this.hobbit // CompA想把打篮球发扬光大...
    }
}
</script>

组件A已经发布了广播了,此时,组件C想拿

// CompC.vue
<template>
    <div> {{ CompAHobbit }} </div> <!-- 打篮球 -->
</template>
<script>
    inject: ['CompAHobbit']
</script>

但是,此时的数据并不是响应式的,如果中途CompAhobbit改成打羽毛球CompC并不知道,显示的还是打篮球

Vue官方.png

所以说要想获得响应式的数据,可以这么写:

// CompA.vue
provide() {
    return {
        CompAHobbit: () => this.hobbit   //广播一个函数,并把结果作为返回值
    }
}
// CompC.vue
inject['CompAHobbit'],
computed: {
    cHobbit() {
        return this.CompAHobbit();
  }
}

优点:

不再需要一层一层的传递数据了,后代想要就要

缺点:

后代只能接收,不能反馈

使用场景:

在一些比较深层的组件中,并且后代不需要反馈给祖先的一些组件库/插件中,也不太建议使用在普通的组件中!因为数据变幻莫测。


4. Event Bus

事件总线,就像一辆公共汽车,你想去见朋友,只要上车就行,然后告诉你朋友你要来了。司机会送你过去,到时候你朋友下来开门,你们就能见面了。

先创建一辆公共汽车

// Bus.js
class Bus{
  es = {}

  // 绑定事件
  on(eventName, cb) {
    this.es[eventName] = this.es[eventName] || [];
    this.es[eventName].push(cb);
  }
  // 触发事件
  emit(eventName, ...params) {
    const listeners = this.es[eventName] || [];
    listeners.forEach(listen => {
      listen(...params);
    });
  }
}

export default new Bus();

或者利用vue这样创建:

// bus.js
 import Vue from 'vue';
 export const bus = new Vue();

或者直接全局创建:

Vue.prototype.$bus = new Vue();

比如两个兄弟组件通信:

// Son1.vue
methods: {
    handleClick() {
        this.$bus.$emit('son1-say', '我是哥哥');
  }
}

Son1 发出信号,Son2监听(接收)

// Son2.vue
mounted() {
    this.$bus.$on('son1-say', who => {
        console.log(who); // 我是哥哥
    }
},
// 记得在组件销毁前移除
beforeDestroy() {
    this.$bus.$off('son1-say');
}

优点:

组件关系不是父子的话,这种方式比第一种方式props来得更加方便。

缺点:

导致的整个事件触发的过程难以追溯,使用的组件一多,数据变化会非常莫名其妙。。

使用场景:

在一些组件库、插件中使用比较香,尽量不要在普通的组件中使用。。
比如有<MyForm />和它的子组件们<MyInput /><MyButton /><MyRadio />,在这个圈子之间传递数据是比较香的。但是脱离了这个生态圈,就别用了。


5. Vuex

重量级选手,能在Vue项目的一切场景使用,但是要安装,成本非常高。
就不详细描述了,大概就是建立一个商店,然后全世界的人都能来这里买卖、自由交易,所有的交易都会被一一记录(Vue DevTools 浏览器插件可以回溯)
具体食用还是看官网吧:Vuex 是什么? | Vuex (vuejs.org)

优点:

大范围杀伤力,无论两个组件隔开有多远,关系多么复杂,都能通信!

缺点:

成本高,Vue官方都说小项目没必要...

使用场景:

如果项目中,两个隔开很远,关系很复杂的组件经常通信,那就用。
比如说,项目一进来,就要获取身份,然后全局共享身份,此时Vuex是很香的。普通组件,插件等,都能用。

总结

  1. 但是如果是父子关系的,首选第一种方式:props
  2. 在封装组件/插件中,兄弟关系的,或者关系略疏远的(比如后代向祖先反馈信息),选择第四种方式:Event Bus
  3. 插件/普通组件中,祖先和后代(非兄弟)关系的,层次比较深,并且后代只接收不反馈的,选择第三中方式:provide、inject;如果需要反馈的,可以联合Event Bus;或者选择第二种方式:$attrs、$listeners。但是这两种方式不要滥用!!!
  4. 普通组件,兄弟通信,你可以props&$emit连用,兄弟经常通信,或者通信稍微复杂的,还是上Vuex吧。

个人喜好:

小项目:props + event Bus
中大项目:props + vuex

其他的,着实鸡肋。。

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

推荐阅读更多精彩内容