开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 18 天,点击查看活动详情
说点题外话
之前用了很多章介绍了父子组件通信:
上一篇说的是选项式非父子组件
说正文
底层原理
provide 和 inject 的实现原理其实是利用了 Vue3 中新的响应式系统和依赖注入机制。
首先,当一个组件使用 provide 来提供数据时,Vue3 会将这些数据包装成一个 reactive 对象,同时将它们添加到当前组件的 provide 实例属性中。这样,子孙组件就可以通过 inject 来访问这些数据了。
具体来说,当一个组件使用 provide 来提供数据时,Vue3 会将这些数据存储在组件实例的 _provided 属性中,同时将 _provided 属性添加到当前组件的依赖项(deps)中。这样,当数据发生变化时,所有依赖它的子孙组件都会被通知更新。
另外,当一个组件使用 inject 来注入数据时,Vue3 会在组件实例的 setup 钩子中创建一个 inject 函数,该函数会在当前组件的依赖项中添加 _provided 属性,并返回其对应的值。这样,子孙组件就可以在模板或组件逻辑中通过 this.xxx 访问这些数据了。
需要注意的是,由于 provide 和 inject 是基于 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.name 和 this.age 访问它们,inject 函数会在依赖项中查找对应的 _provided 属性,并返回其值。
当父组件提供的数据发生变化时,由于它们是 reactive 对象,所以会触发响应式更新,Vue3 会遍历所有依赖它的子孙组件,调用它们的 render 函数进行更新。