美文网首页
《Vue》组件各种通讯方式的使用场景

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

作者: BA_凌晨四点 | 来源:发表于2021-07-18 00:04 被阅读0次

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

其他的,着实鸡肋。。

相关文章

网友评论

      本文标题:《Vue》组件各种通讯方式的使用场景

      本文链接:https://www.haomeiwen.com/subject/knrwpltx.html