和Vue3和解的Day18--非父子组件通信

84 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 18 天,点击查看活动详情

说点题外话

之前用了很多章介绍了父子组件通信:

选项式父组件向子组件传参

选项式父子非props组件通信

选项式子组件向父组件传参

组合式子组件向父组件传参

上一篇说的是选项式非父子组件

和Vue3和解的Day16--非父子组件通信

说正文

image.png

底层原理

provideinject 的实现原理其实是利用了 Vue3 中新的响应式系统和依赖注入机制。

首先,当一个组件使用 provide 来提供数据时,Vue3 会将这些数据包装成一个 reactive 对象,同时将它们添加到当前组件的 provide 实例属性中。这样,子孙组件就可以通过 inject 来访问这些数据了。

具体来说,当一个组件使用 provide 来提供数据时,Vue3 会将这些数据存储在组件实例的 _provided 属性中,同时将 _provided 属性添加到当前组件的依赖项(deps)中。这样,当数据发生变化时,所有依赖它的子孙组件都会被通知更新。

另外,当一个组件使用 inject 来注入数据时,Vue3 会在组件实例的 setup 钩子中创建一个 inject 函数,该函数会在当前组件的依赖项中添加 _provided 属性,并返回其对应的值。这样,子孙组件就可以在模板或组件逻辑中通过 this.xxx 访问这些数据了。

需要注意的是,由于 provideinject 是基于 Vue3 的响应式系统和依赖注入机制实现的,因此它们仅在 Vue3 中有效,而在 Vue2 或其它框架中并不适用。

接下来我们简单演示一下

首先,在父组件中使用 provide 提供数据:

// 父组件中提供数据
export default {
  provide() {
    return {
      name: 'juejin',
      age: 2
    }
  }
}

接着,Vue3 会将提供的数据包装成一个 reactive 对象,并将其存储在当前组件实例的 _provided 属性中:

    父组件实例
┌───────────────────────────────┐
│       _provided: reactive     │
│    ┌─────────────┬────────┐  │
│    │   name: 'juejin'      │  │
│    │   age: 2               │  │
│    └─────────────┴────────┘  │
└───────────────────────────────┘

同时,Vue3 会将 _provided 属性添加到当前组件的依赖项中:

    父组件实例
┌───────────────────────────────┐
│       _provided: reactive     │
│    ┌─────────────┬────────┐  │
│    │   name: 'juejin'      │  │
│    │   age: 2               │  │
│    └─────────────┴────────┘  │
│            deps: Set          │
│    ┌─────────────┬────────┐  │
│    │ _provided   │  Set   │  │
│    └─────────────┴────────┘  │
└───────────────────────────────┘

然后,在子组件中使用 inject 注入数据:

export default { inject: ['name', 'age'], mounted() { console.log(`My name is ${this.name} and I am ${this.age} years old.`) } }

Vue3 会在子组件的 setup 钩子中创建一个 inject 函数,并将其添加到当前组件的依赖项中:

    子组件实例
┌───────────────────────────────┐
│            deps: Set          │
│    ┌─────────────┬────────┐  │
│    │ _provided   │  Set   │  │
│    │ injectFn    │  Set   │  │
│    └─────────────┴────────┘  │
└───────────────────────────────┘

当子组件需要访问父组件提供的数据时,它就可以通过 this.namethis.age 访问它们,inject 函数会在依赖项中查找对应的 _provided 属性,并返回其值。

当父组件提供的数据发生变化时,由于它们是 reactive 对象,所以会触发响应式更新,Vue3 会遍历所有依赖它的子孙组件,调用它们的 render 函数进行更新。