Vue3源码runtime-core运行时核心模块之provide依赖和inject注入详解
概览
vue3中提供了一种父子组件(也包括爷孙组件)的通信方式,即依赖provide
和注入inject
。父子组件固然可以通过属性以及emit
实现数据交换,但是有一个弊端就是,只能逐层传递,而provide
/inject
只要是存在组件嵌套关系,父子关系的延续,就可以应用。
源码分析
provide
和inject
是是直接可以从vue3中导入使用的方法,实现文件路径:packages\runtime-core\src\apiInject.ts
provide
provide
方法就是往实例instance
的provides
属性中新增键值对。
provide
的源码实现如下:
function provide(key, value) {if (!currentInstance) ; else {let provides = currentInstance.provides;const parentProvides = currentInstance.parent && currentInstance.parent.provides;if (parentProvides === provides) {provides = currentInstance.provides = Object.create(parentProvides);}provides[key] = value;}
}
provide
方法接收键值对key
,value
;若是当前实例currentInstance
不存在,则直接return
;前面提到数据都是挂载到实例上。然后获取实例的provides
,获取当前实例的父实例上的provides
,然后判断parentProvides
与provides
是否相等,若相等,则说明二者都是null
,然后通过Object.create
创建一个纯净的对象;最后就是赋值。
在vue3的源码createComponentInstance
即创建组件实例中,也是先判断parent.provides
,若存在,则将父组件的provides
赋值给当前实例的provides
;若不存在则通过Object.create
创建。
function createComponentInstance(vnode, parent, suspense){const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;const instance = {provides: parent ? parent.provides : Object.create(appContext.provides),}return instance;
}
inject
inject
方法就是从provides
中提供的数据获取key
对应值,因为父子组件实例的provides
是一条原型链,若父组件中不存在,则会继续查找父组件的父组件上是否存在。其实现如下:
function inject(key, defaultValue, treatDefaultAsFactory = false) {const instance = getCurrentInstance();if (instance || currentApp) {let provides = currentApp ? currentApp._context.provides : instance ? instance.parent == null || instance.ce ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides : void 0;if (provides && key in provides) {return provides[key];} else if (arguments.length > 1) {return treatDefaultAsFactory && shared.isFunction(defaultValue) ? defaultValue.call(instance && instance.proxy) : defaultValue;} else ;}
}
inject
方法接收三个参数:key
键/defaultValue
默认值/treatDefaultAsFactory
是否作为工厂函数。先是调用getCurrentInstance()
获取当前的组件实例,然后判断组件实例或应用实例是否存在,若存在,则通过一些三元操作来获取provides
,然后获取对应的值;若provides
中不存在key
属性,且参数超过1个,则判断默认值是否是函数以及是否是工厂模式,若是,则调用该函数,this
为组件实例的代理对象;否则返回默认值。