Vue源码解析:Vue生命周期之从生到死(三)
上俩片文章我们讲到了initLifecycle和initEvents函数,本篇是时候讲讲initInjections初始化函数了。该函数就是用来去实例化inject选项。说到inject,那就必然离不开provide,这俩是成对出现的,它们的作用是:允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
provide:是一个对象,或者是一个返回对象的函数。
inject:是一个字符串数组,或者是一个多层对象。
这俩个东西,在组件中用的比较多。总结来说,就是父组件可以使用provide
选项给自己的下游子孙组件内注入一些数据,在下游子孙组件中可以使用inject
选项来接收这些数据以便为自己所用。provide
和 inject
选项绑定的数据不是响应式的。
initInjections是何方神圣?
我们发现,provide和inject函数是成对出现,但是,在init的初始化中发现,provide和inject之间有一个initstate函数,其实不然,provide
选项注入的值作为数据入口,也就是data
、props
、watch
、computed
及method,所以inject之前可能会被以上的这些使用到,所以在初始化inject后需要初始化这些数据,然后才能初始化provide。
在调用initInjections
函数对inject
初始化完之后需要先调用initState
函数对数据进行初始化,最后再调用initProvide
函数对provide
进行初始化。
initInjections函数的原理:
// src/core/instance/inject.js
export function initInjections (vm: Component) {
const result = resolveInject(vm.$options.inject, vm)
if (result) {
toggleObserving(false)
Object.keys(result).forEach(key => {
defineReactive(vm, key, result[key])
}
toggleObserving(true)
}
}
export let shouldObserve: boolean = true
export function toggleObserving (value: boolean) {
shouldObserve = value
}
首先调用resolveInject
把inject
选项中的数据转化成键值对的形式赋给result,result的结果应该是这样的。
// 父级组件提供 'foo'
var Parent = {
provide: {
foo: 'bar'
}
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
}
// result
result = {
'foo':'bar'
}
然后遍历result
中的每一对键值,调用defineReactive
函数将其添加当前实例上。我们发现遍历之前调用了toggleObserving(false)函数,这是在告诉defineReactive函数仅仅是把键值添加到当前实例上而不需要将其转换成响应式。要不然如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
resolveInject又干了什么?
export function resolveInject (inject: any, vm: Component): ?Object {
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._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent
}
if (!source) {
if ('default' in inject[key]) {
const provideDefault = inject[key].default
result[key] = typeof provideDefault === 'function'
? provideDefault.call(vm)
: provideDefault
} else if (process.env.NODE_ENV !== 'production') {
warn(`Injection "${key}" not found`, vm)
}
}
}
return result
}
}
首先创建一个空对象result
,用来存储inject
选项中的数据key
及其对应的值,作为最后的返回结果。然后获取当前inject
选项中的所有key
,然后遍历每一个key
,拿到每一个key
的from
属性记作provideKey
,provideKey
就是上游父级组件提供的源属性,然后开启一个while
循环,从当前组件起,不断的向上游父级组件的_provided
属性中(父级组件使用provide
选项注入数据时会将注入的数据存入自己的实例的_provided
属性中)查找,直到查找到源属性的对应的值,将其存入result
中。如果没有找到,那么就看inject
选项中当前的数据key
是否设置了默认值,即是否有default
属性,如果有的话,则拿到这个默认值,官方文档示例中说了,默认值可以为一个工厂函数,所以当默认值是函数的时候,就去该函数的返回值,否则就取默认值本身。如果没有设置默认值,则抛出异常。最后返回result。
ok,之前说到inject是一个字符串数组,但是没有处理,这是为什么呢?其实在初始化阶段_init
函数在合并属性的时候还调用了一个将inject
选项数据规范化的函数normalizeInject。
这样做的目的是,不管用户使用了何种写法,统统将其转化成一种便于集中处理的写法。
function normalizeInject (options: Object, vm: ?Component) {
const inject = options.inject
if (!inject) return
const normalized = options.inject = {}
if (Array.isArray(inject)) {
for (let i = 0; i < inject.length; i++) {
normalized[inject[i]] = { from: inject[i] }
}
} else if (isPlainObject(inject)) {
for (const key in inject) {
const val = inject[key]
normalized[key] = isPlainObject(val)
? extend({ from: key }, val)
: { from: val }
}
} else if (process.env.NODE_ENV !== 'production') {
warn(
`Invalid value for option "inject": expected an Array or an Object, ` +
`but got ${toRawType(inject)}.`,
vm
)
}
}
这个函数的逻辑不复杂,也是一目了然,就是在处理传入的inject,将其格式化。。。。
未完,待续。。。
本文地址:https://blog.csdn.net/leelxp/article/details/107131649
上一篇: 女性如何保养,每月四期保养五十岁都不显老
下一篇: Python入门必知的几个点