面试官:说一下组件通信的方式
我:provide/inject , ... ...
面试官:简单说一下provide/inject原理
我:啊,卒......
1.provide/inject使用场景
inject和provide一般都是一起使用的,允许祖先组件向其所有子孙后代注入依赖,不论组件的层次有多深,依赖始终生效的。
inject:可以是字符串,对象,以及数组,用于可以在注入内容中搜索。provide:可以为一个对象或者一个返回对象的函数,用于注入依赖。
const parent = {
provide:{
a:'aaa'
}
}
const child = {
inject:['a'],
created(){
console.log(this.a) // 'aaa'
}
}
同时,我们也可以在data和props种访问注入的值
const child = {
inject:['a','b'],
props:{
propA:{
default(){
return this.a
}
}
},
data(){
return {
dataB:this.b
}
}
}
我们还可以通过form来表示其源属性,default代表默认值:
const child = {
inject:{
aa:{
from:"a",
default:"bb"
}
}
}
2.inject内部原理
我们知道inject和provide是成对出现的,但是他们的内部实现却是分开的。从之前示例中我们就发现,inject在data/props之前初始化的,而provide在data/props后面初始化的。目的是可以让data/props依赖于inject,所以inject的初始化要放在前面。provide在后面也很好理解,因为provide依赖这些值。
2.1 initInjections
很明显,初始化inject的值,就是读取配置的key值,读不到则先父级读下去。是一个自底向上获取的过程。最终找到的内容保存到实例上即可。因为这些值会保存到实例上,所以我们还需要对这些值进行响应式。
export function initInjections (vm) {
const result = resolveInject(vm.$options.inject,vm)
Object.keys(result).forEach((key)=>{
defineReactive(vm,key,result[key])
})
}
2.2 resolveInject
resolveInject这个函数主要就是从下向上进行查找的操作。
export function resolveInject(inject,vm){
if(inject){
const result = Object.create(null);
const keys = Object.keys(inject)
for(let i=0;i<keys.length;i++){
const key = keys[i]
const provideKey = inject[key].from
let source = vm
while(source){
if(source.provide && provideKey in source.provide){
result[key] = source.provide[provideKey];
break;
}
source = source.$parent
}
}
return result
}
}
我们通过for循环拿到每个key值,通过from属性拿到provide源属性。通过while循环来搜索内容,如果找到源值就可以跳出循环。进行下一轮循环查找。
3总结:
inject在data/props之前进行初始化(方便data/props可以依赖inject)inject通过自下而上的方式拿到源值(provide提供的值)- 拿到所有源值后进行本实例的响应式绑定