ToC
hasInjectionContext()
hasInjectionContext()
方法主要用于判断当前是否具有注入上下文环境, currentInstance
和 currentRenderingInstance
都是指组件,currentApp
则表示的是应用根组件,如果没有获取到 currentInstance
则判断根应用实例是否初始化:
1export function hasInjectionContext(): boolean {2 return !!(currentInstance || currentRenderingInstance || currentApp)3}
provide()
provide()
函数的实现比较简单,删除ts类型及原代码注释,再加上我自己的理解以后代码如下:
1export function provide(key, value) {2 // 如果没有获取到当前的组件实例则表示在组件外调用的 provide()3 if (!currentInstance) {4 // 为了减少生产包的体积,将提示设置为只在开发环境打印5 // 打包生产包时, __DEV__ 变量会被替换为 false, 这样就会被 tree-shanking 丢弃6 if (__DEV__) {7 warn(`provide() can only be used inside setup().`)8 }9 } else {10 // 先获取当前组件的 provides15 collapsed lines
11 let provides = currentInstance.provides12
13 // 再尝试获取父级组件的 provides14 const parentProvides = currentInstance.parent && currentInstance.parent.provides15
16 // 如果他们相等,则可能是在创建页面级组件,将父级的provides覆盖当前的 provides17 // 确保所有子组件都是用的最上层的 provides,保证他们的引用相同,从而实现无限层级的上下文透传18 if (parentProvides === provides) {19 provides = currentInstance.provides = Object.create(parentProvides)20 }21
22 // 获取到 provides 以后将当前的值增加上去23 provides[key] = value24 }25}
inject()
inject()
在实现上会比 provide()
稍加复杂一点,但也没有太复杂,多的那部分判断只是用来获取当前组件上下文的代码而已,同样的,删除ts类型及原注释,加上我自己的代码理解以后代码如下:
1export function inject(key, defaultValue, treatDefaultAsFactory = false) {2 // 回退到 `currentRenderingInstance`,以便可以在功能组件中调用它3 const instance = currentInstance || currentRenderingInstance4
5 // 如果找不到当前正在渲染的组件,那就从根应用上获取6 if (instance || currentApp) {7 // https://github.com/vuejs/core/issues/24008 // 支持在 app.use 的插件里使用 inject,如果找不到则一直向上回退,直到根应用app9 const provides = instance10 ? instance.parent == null24 collapsed lines
11 ? instance.vnode.appContext && instance.vnode.appContext.provides12 // 因为父级的provides会一路将其组件级的provides作为原型,所以只要找父级的就能拿到所有的 provide item13 : instance.parent.provides14 : currentApp!._context.provides15
16 // 如果是正常提供的 provides,并且找到了对应的key则正常返回17 if (provides && key in provides) {18 return provides[key]19 } else if (arguments.length > 1) {20 // 如果参数位大于1,同时 treatDefaultAsFactory 设置为true,则表示 defaultValue 可能是个功能函数21 return treatDefaultAsFactory && isFunction(defaultValue)22 // 如果是函数,则将当前获取到的组件实例当作this指向获取它的返回值23 ? defaultValue.call(instance && instance.proxy)24 // 如果没有设置 treatDefaultAsFactory 则直接返回 defaultValue, 没有设置则 undefined25 : defaultValue26 } else if (__DEV__) {27 // 如果没有找到则在开发环境下提示未找到28 warn(`injection "${String(key)}" not found.`)29 }30 } else if (__DEV__) {31 // 没有找到组件实例则表示可能是在组件外部调用的方法32 warn(`inject() can only be used inside setup() or functional components.`)33 }34}
总结
这就是这两个API的全部实现,整体看下来还是很简单的,provide()
就是一直继承父级的 provides 作为自己的,利用原型链的特性一直向上查找,inject()
则是获取当前的组件实例,然后从实例上获取 provides,如果没有找到则在开发环境打印警告,hasInjectionContext()
方法则是判断当前组件/根应用是否已经挂载,都没有的话则表示应用没有初始化,不具备调用 provide()
和 inject()
的条件。
以上。