它们一起有个响亮的名字叫依赖注入,provide
提供数据和key, inject
注入key获取数据,它们的出现是为了解决prop 逐级透传
问题, 一个父组件相对于其所有的后代组件,会作为依赖提供者(provide)。任何后代的组件树,无论层级有多深,都可以注入(inject)由父组件提供给整条链路的依赖
功能特点
通过父级使用provide,子级可以不管有多深都可以通过inject拿到父级中提供的依赖数据
源码简化实现
function provide(key, value) {
let provides = getCurrentInstance().provides
// 获取父级provides
const parentProvides =currentInstance.parent && currentInstance.parent.provides
// 默认情况下,实例继承其父类的providers对象,
// 但是当它需要提供自己的值时,它创建它的,使用parent,提供对象作为原型。
if (parentProvides === provides) {
// 继承父级provides
provides = currentInstance.provides = Object.create(parentProvides)
}
provides[key] = value
}
// inject就更简单了只要获得父级的实例上的provides,再通过key取值
function inject(key) {
const instance = currentInstance || currentRenderingInstance
if (instance) {
const provides = instance.parent && instance.parent.provides
if (provides && key in provides) {
return provides[key]
}
}
}
以上是vue中的依赖注入的简化实现,一些边界代码都删了,这个两实现替换vue中的provide和inject,在一定场景下是可以使用的如下:
// 父级中
provide('abc', 9)
// 子级中
inject('abc') // 能获取到9
组件创建时会对provides初始化代码, 可以看到,默认情况下,实例继承其父类的providers对象
function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null
) {
const type = vnode.type as ConcreteComponent
const appContext =
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
const instance: ComponentInternalInstance = {
// ...
// 默认情况下,实例继承其父类的providers对象
provides: parent ? parent.provides : Object.create(appContext.provides),
// ...
}
// ...
return instance
}
总结
1、 组件实例时会初始provides属性,默认为父级的provides 2、 在组件setup中使用provide提供依赖,这时第1步已完成,要是子组件也用provide时,它会重新创建它,使用parent的provides作为其原型,并添加依赖 3、 inject取其父级的provides属性,并通过传入的key从provides中获取值
实现还是比较简单,源码里面还有很多细节和边界处理,但理解其精髓还是够了的
如有问题,欢迎留言交流