vue源码inject.js

241 阅读1分钟

vue api中提供了provide/inject,那么inject中是如何接收到provide的呢?

/* @flow */
import { hasOwn } from 'shared/util'
import { warn, hasSymbol } from '../util/index'
import { defineReactive, toggleObserving } from '../observer/index'

export function initProvide (vm: Component) {
  // 取provide
  const provide = vm.$options.provide

  if (provide) {
    // 如果是方法,执行注入this = vm
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

export function initInjections (vm: Component) {
  // 处理inject
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    // shouldObserve切换为false,在defineReactive中,调用observe()时跳过new Observe()
    toggleObserving(false)
    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)
  }
}

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)

    // Reflect.ownKeys(target)返回自身属性key,相当于Object.keys(),但不会受到enumerable影响
    // 详细文档见
    // [Reflect - JavaScript | MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/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...
      if (key === '__ob__') continue

      // TODO 在什么时候挂上的from
      const provideKey = inject[key].from
      // 查找provide过程
      // 当前组件是否存在provideKey,如果有给到result,跳出
      // 父级组件赋给source,继续上面的操作
      // 直到无父级组件
      let source = vm
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      
      if (!source) {
        // inject如果存在default属性,即默认值
        if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          // 如果default是方法,注入vm执行
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== 'production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}

总结

查找provide过程:

# 1. result存储查找到的provide设置
# 2. 循环当前组件inject的keys,当前inject key 为 provideKey
# 3. 当前组件是否存在provideKey,如果有给到result,跳出
# 4. 父级组件赋给source,继续上面的2,3操作
# 5. 直到无父级组件

#朝花夕拾/Front-end/综合应用/vue/源码