Vue 100 问 (30-33问)Vue 组件之间的通信方式有哪些?

3,585 阅读5分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。

30. 举一个$attrs$listeners 的应用场景

比如,有 A、B、C 三个组件,调用关系如下: A -> B -> C

B 调用 C 时,在 C 上写$attrs$listeners,就可以把 C 的属性和事件绑定在 B身上,A 调用 B 时,就可以拿到 C 上的属性和事件。

这种写法在对一些组件进行二次封装时非常有用,比如,对 element-ui 的级联选择器组件 el-cascader 进行二次封装,只改了很小的一个功能,其他功能保持原样。

就可以定义一个 custom-cascader 组件

custom-cascader 内部
...
<el-cascader
  v-bind="$attrs"
  v-on="$listeners"
  ...
/>
...
调用 custom-cascader 的地方
<custom-cascader
  :options="xxx"
  size="mini"
  @remove-tag="xxx"
/>

image.png

image.png

这些 el-cascader 的属性和事件都可以直接使用,不用再通过 props 传递一层了,非常方便。

31. 用过 provide 和 inject 吗?

provide 和 inject 一般用于层级很深的组件之间通信。

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。 -- vue文档

日常的业务组件一般不会用,因为容易造成混淆,而且一般的项目都用了 Vuex

provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。 -- vue文档

正如文档所说, provide 和 inject 用来开发组件库很有用,因为一般开发组件库不会引类似于 Vuex 这样的三方依赖,就只有用 Vue 本身提供的 provide 和 inject 了。

比如, element-ui 的源码中就大量地使用到了 provide 和 inject。

image.png

而且他的用法是直接把这个组件实例 provide 过去,像这样

image.png

需要用到的地方,直接调这个实例上的属性和方法

image.png

provide 和 inject 基本使用

当然,vue 文档虽然不推荐我们在业务开发中用,但有时候用一用还是无伤大雅的,provide 和 inject 基本使用如下:

// A.vue
export default {
  provide: {
    name: 'lin'
  }
}

// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // lin
  }
}

可以看到,在 A.vue 里,我们设置了一个 provide: name,值为 lin,它的作用就是将 name 这个变量提供给它的所有子组件。

而在 B.vue 中,通过 inject 注入了从 A 组件中提供的 name 变量,那么在组件 B 中,就可以直接通过 this.name 访问这个变量了,它的值也是 lin。

32. provide 和 inject 是响应式的吗?

provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。 -- vue 文档

写个 demo 试一试:

// 父组件内:
<Child />
<button @click="changeName">change</button>
data() {
  return {
    name: 'lin',
    person: {
      name: 'Allen'
    }
   }
},
provide() {
  return {
    name: this.name,
    person: this.person
  }
},
methods: {
  changeName() {
    this.name = 'lin12345'
    this.person.name = 'Adel'
  }
}
// 子组件内:
<p>{{ name }}</p>
<p>{{ person }}</p>

inject: ['name', 'person']

provide.gif

果然如此,一般的变量不可响应,传入对象,对象的属性可以响应。

33.Vue 组件之间的通信方式有哪些?

常用的:

  • 父子组件交互,props$emit,vue单向数据流。

  • 使用$attrs$listeners,集成子组件的属性和事件,在对一些组件进行二次封装时非常有用,可以不用写具体的属性或事件名,直接获取。

  • 使用Vuex进行状态管理

不常用的:

  • 使用$refs获取组件实例,进而获取数据

  • 组件中可以使用 $parent$children 获取到父组件实例和子组件实例,进而获取数据

  • 使用eventBus进行跨组件触发事件,进而传递数据,一般用得比较少

  • 使用provideinject,一般用于基础组件开发,或用于跨很多层的组件传值。

  • 通过 mixins 传公共逻辑。

其实,像provide/inject还有 mixins这种通信方式,写组件库没啥,但最好不要用在业务组件里。

因为项目复杂了之后,很容易造成迷惑,不知道数据是从哪里传递过来的,全局搜调用的属性名或者函数名,结果出来很多个,太令人难受了。

如果是通过文件夹的形式管理业务组件,每个文件夹有自己内部的mixins,这种方式还是可以接受的。

image.png

但其实也很难受,日常代码开发由上下横跳变成左右横跳,不过这样可以让我们的单个文件代码少一点,还是要优雅一点。

$refs$parent$children,我一般也用得少,除非迫不得已一定要用,因为这些组件通信方式用了之后,数据的流向不是一目了然的,在这个文件里搜属性或者方法,搜不到来源,要把这个文件的代码仔细读一下才能清楚数据流向,开发效率就降低了。

如果觉得文章对你有帮助,就给我一个小小的赞吧^ _ ^

往期

从 keep-alive 源码掌握 LRU Cache

Vue 100问(27-29)说一下你对 vue 插槽的理解

Vue 100问(23-26问)vue 指令的本质是什么?

Vue 100问(18-22问)谈谈你对 Vue 生命周期的理解

Vue100问(第6-17问)

Vue100问(第5问)为什么 v-for 的 key 值不能是 index?

Vue100问(第4问)说一下你平时用过的 Vue 修饰符

Vue100问(2-3问)什么场景下会使用.sync修饰符?

Vue100问(一)不使用v-model,如何实现双向绑定?