vlambda博客
学习文章列表

Vue 源码解析 (五)初始化注入流程

最近好久没有更新文章了,最近忙于公司的事情,下班忙于自己的产品,掏空了身体



Vue 源码解析 (五)初始化注入流程

hasSymbol

先来看看 Vue 是如何实现这个方法的

export function isNative (Ctor: any): boolean {
  return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}
export const hasSymbol =
  typeof Symbol !== 'undefined' && isNative(Symbol) &&
  typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys)

resolveInject

解析传递进来的 inject options

我们先来看看官网的一些例子:

  • default 是函数的情况
const Child = {
  inject: {
    foo: {
      from'bar',
      default() => [123]
    }
  }
}
  • default 是 字符串的情况
const Child = {
  inject: {
    foo: {
      from'bar',
      default'foo'
    }
  }
}

所以代码需要做如下处理:

  • 处理 default
  • 处理 from
export function resolveInject(inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    // 创建一个 空对象
    const result = Object.create(null)
    // 判断是否是 Symbol,Reflect
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)

    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // #6574 in case the inject object is observed...
      // 去除 key === '__ob__' 的情况
      if (key === '__ob__'continue
      // 获取 from 属性 key
      const provideKey = inject[key].from
      let source = vm
      // 循环获取保存 key
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      // 处理 default 属性 key 的情况
      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
  }
}

initInjections

循环代理

export function initInjections(vm: Component{
  // 拿到解析的结果
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    toggleObserving(false)
    // 将属性逐个代理到 vm 对象上,并做响应式处理
    Object.keys(result).forEach(key => {
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        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)
  }
}

initProvide

处理我们传递进来的 provide options 属性,

  • 可以是对象,
  • 可以是方法
export function initProvide(vm: Component{
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}