当前位置: 移动技术网 > IT编程>脚本编程>vue.js > Vue源码解析:Vue生命周期之从生到死(三)

Vue源码解析:Vue生命周期之从生到死(三)

2020年07月09日  | 移动技术网IT编程  | 我要评论

上俩片文章我们讲到了initLifecycle和initEvents函数,本篇是时候讲讲initInjections初始化函数了。该函数就是用来去实例化inject选项。说到inject,那就必然离不开provide,这俩是成对出现的,它们的作用是:允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

provide:是一个对象,或者是一个返回对象的函数。

inject:是一个字符串数组,或者是一个多层对象。

这俩个东西,在组件中用的比较多。总结来说,就是父组件可以使用provide选项给自己的下游子孙组件内注入一些数据,在下游子孙组件中可以使用inject选项来接收这些数据以便为自己所用。provide 和 inject 选项绑定的数据不是响应式的。

initInjections是何方神圣?

我们发现,provide和inject函数是成对出现,但是,在init的初始化中发现,provide和inject之间有一个initstate函数,其实不然,provide选项注入的值作为数据入口,也就是datapropswatchcomputedmethod,所以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
}

首先调用resolveInjectinject选项中的数据转化成键值对的形式赋给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,拿到每一个keyfrom属性记作provideKeyprovideKey就是上游父级组件提供的源属性,然后开启一个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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网