Vue2 的 provide
和 inject
通过组件实例的 _provided
属性存储数据,注入时递归向上查找父链,结合响应式系统实现跨层级通信,数据流向为单向自上而下。
每个组件实例通过 _provided
属性存储自己提供的值。
// 源码位置:src/core/instance/inject.js
function initProvide(vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
// 源码位置:/src/core/instance/inject.ts
export function initInjections(vm: Component) {
const result = resolveInject(vm.$options.inject, vm) // 向上自己的父亲中递归查找对应的属性
if (result) {
toggleObserving(false)
Object.keys(result).forEach(key => {
/* istanbul ignore else */
if (__DEV__) {
defineReactive(vm, key, result[key], () => {
warn(
`Avoid mutating an injected value directly since the changes will be ` +
`overwritten whenever the provided component re-renders. ` +
`injection being mutated: "${key}"`,
vm
)
})
} else {
defineReactive(vm, key, result[key])
}
})
toggleObserving(true)
}
}
当组件需要注入数据时,会从当前组件开始,逐级向上查找父组件的 _provided
属性,直到找到匹配的键。
// 源码关键逻辑:src/core/instance/inject.js
function resolveInject(inject: Object, vm: Component): Object {
const result = {}
for (const key in inject) {
let source = vm
while (source) {
if (source._provided && hasOwn(source._provided, key)) {
result[key] = source._provided[key] // 拿到结果存在 result
break // 找到一个就停止 e.g. 爷爷组件 provide: a: 1, 父亲组件 provide: a: 2 那么子组件通过 inject: a 拿到的是父亲组件的 a=2 而不是爷爷组件的 a=1
}
source = source.$parent // 向上遍历父链
}
}
return result
}