vue中provide和inject的实现

43 阅读2分钟

它们一起有个响亮的名字叫依赖注入,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中获取值

实现还是比较简单,源码里面还有很多细节和边界处理,但理解其精髓还是够了的

如有问题,欢迎留言交流