VUE2进阶至VUE3六(依赖注入&EventBus)

2,081 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

那些兄弟,父子,爷孙。。。的沟通方式,详细请见下文

Provide/Inject(依赖注入)

  • 介绍:

多用于层级较多的组件通讯中,当多层级组件嵌套时,最外层级的组件需要向最里面的组件传值时,可以用到Provide/Inject,这样既不会影响这条链路上的其他组件,最里面的子组件也可以顺利拿到最外层组件传出来数据。

  • 使用: 父组件通过Provide将数据传出,Provide第一个参数时键名,第二个参数为键值,键值需要使用reactiveref包裹将其变为响应式数据。随后子组件通过Inject接收,但是子组件再修改值时会报错,所以需要在获取时候给一个默认值。

代码示例如下:

父组件:

<template>
  <div class="home">
    Home
    <button>Home</button>
    <Main></Main>
  </div>
</template>
<script setup lang="ts">
  import {  reactive, ref, provide } from 'vue'
  import Main from './main.vue'
  let mesg = ref<string>('jiang');
  provide('mesg', mesg);
</script>

子组件

<template>
   <div style="background:yellow;">
     Main
     <button @click="handleClick">bian</button>
     <div>{{mesg}}</div>
    </div>
</template

<script setup lang="ts">
  import { inject,Ref, ref } from 'vue';
  let mesg = inject<Ref<string>>('mesg', ref('jxx')) // 这里ref('jxx')默认值就是防止报错的,因为mesg.value又可能为undefined
  const handleClick = () => {
    mesg.value = 'hah'
  }
</script>

兄弟组件传值

兄弟组件是如何进行通话的,可以直接通话吗?还是要借助“爸爸”,反正,我是不想第三方替我传话,太麻烦,相信兄弟之间肯定有些不想被他人知道的小秘密吧😉。下面就来说道说道公开谈话与私密谈话的不同方式吧~

  • 第一种方法,通过一个父组件作为桥梁,为两个子组件传话,实现兄弟组件的传值。不过,这种传值方式有些麻烦并不是很推荐。(不想让“爸爸”作为传话筒,那就试试第二种方法吧~)
  • 第二种方法,通过eventBus,下面是一个博主实现的简易的bus

type BusClass<T> = {
  emit: (name: T) => void
  on: (name: T, callback: Function) => void
}
type BusParams = string | number | symbol
type List = {
  [key: BusParams]: Array<Function>
}
class Bus<T extends BusParams> implements BusClass<T> {
  list: List
  constructor() {
      this.list = {}
  }
  emit(name: T, ...args: Array<any>) {
      let eventName: Array<Function> = this.list[name]
      eventName.forEach(ev => {
          ev.apply(this, args)
      })
  }
  on(name: T, callback: Function) {
      let fn: Array<Function> = this.list[name] || [];
      fn.push(callback)
      this.list[name] = fn
  }
}

export default new Bus<number>()

使用:

在需要派发的组件通过emit将数据派发出去,然后再需要接收的组件通过on进行接收。

home组件:

<template>
  <div class="home">
    <button @click="send1">发送</button>
  </div>
</template>
<script setup lang="ts">
  import {  reactive, ref, provide } from 'vue'
  import Bus from '../Bus'
  let flag = false;
  const send1 = () => {
    flag = !flag
    Bus.emit('handleClick', flag)
  }
</script>

main组件:

<template>
   <div style="background:yellow;">
     {{Flag}}
    </div>
</template>
<script setup lang="ts">
  import { inject,Ref, ref } from 'vue';
  import Bus from '../Bus'
  let Flag = ref(false)
  Bus.on('handleClick', (flag:boolean) => {
    Flag.value = flag;
  })
</script>

App.vue

<template>
  <Home />
  <Main></Main>
</template>
<script setup lang="ts">
import Home from './views/home.vue'
import Main from './views/main.vue'
</script>

或者如果不想自己实现bus,那么这里推荐一个插件mitt,正常使用即可。

简单介绍:首先下载安装mittyarn add mitt,然后新建一个ts文件导出mitt实例即可。

import mitt from 'mitt';
export default mitt();

总结

在这里说一点题外话,Vue3并不是特别推荐全局变量,特别时针对TS的全局API,尤大也有相关说明不建议全局APITS中使用。

image.png

在需要数据的情况下,可以在根组件通过Provide方法将数据派发出去,然后在需要的组件通过Inject去获取,或者使用EventBusvuex来解决。当然,也只是推荐,如果一定要定义全局变量就需要用到getCurrentInstance,有兴趣的伙伴移步官网一探究竟吧~

最后,其实组件传值有很多方式,我们要注意甄别哪些是比较符合当前功能的传值方法,后面还会介绍其他组件传值方式,好啦~ 今天的学习就到这里啦~ 后续请听下回分解😏