Vue2 的 provide 和 inject 通过组件实例的 _provided 属性存储数据,注入时递归向上查找父链,结合响应式系统实现跨层级通信,数据流向为单向自上而下。

Provide

每个组件实例通过 _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
  }
}

Inject

// 源码位置:/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
}