Vue2.x源码学习笔记(三)——initInjections和initProvide

209 阅读1分钟

initInjections

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)
  }
}

resolveInject函数的作用是通过用户配置的inject自底向上搜索可用的注入内容,并将搜索结果返回。循环result通过defineReactive将inject响应式的添加到实例上,但对于值不需要进行响应式处理。

resolveInject

function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    const result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)

    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      if (key === '__ob__') continue
      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
  }
}

获取inject的key时需要兼容处理key为symbol类型。循环keys,如果时__ob__则跳过。获取源属性provideKey,从当前实例开始while循环。如果源属性在source的 _provided(当使用provide注入内容时,其实是将内容注入到当前组件实例的 _provide)中能找到对应的值,那么将其设置到result中,并使用break跳出循环。否则,将source设置为父组件实例进行下一轮循环。

如果没有找到源头,那就看这个inject有没有default默认值,有的话判断默认值类型,是函数就调用函数,最终获取默认值。如果没找到源头也没有默认值就抛出错误。

initProvide

function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

将用户的provide存放到实例的_provided中,如果是函数就调用函数。