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;
}
}
2
3
4
5
6
7
8
9
10
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;
}
2
3
4
5
6
7
8
# 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 ;
}
}
2
3
4
5
6
7
8
9
10
11
inject
方法接收三个参数:key
键/defaultValue
默认值/treatDefaultAsFactory
是否作为工厂函数。先是调用getCurrentInstance()
获取当前的组件实例,然后判断组件实例或应用实例是否存在,若存在,则通过一些三元操作来获取provides
,然后获取对应的值;若provides
中不存在key
属性,且参数超过1个,则判断默认值是否是函数以及是否是工厂模式,若是,则调用该函数,this
为组件实例的代理对象;否则返回默认值。