Home  >  Article  >  Web Front-end  >  Vue.js source code learning provide and inject

Vue.js source code learning provide and inject

小云云
小云云Original
2018-02-24 14:25:581600browse

Added provide and inject options in the 2.2.0+ version of Vue.js. They appear in pairs and are used to pass data down from the parent component.

Source code location

As before, the initialization methods are all in Vue’s _init method.

  // src/core/instance/init.js
  Vue.prototype._init = function (options?: Object) {
    ……
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')
  }

Find the initInjections and initProvide methods here. This is the initialization method of provide and inject. Both methods are in src/core/instance/inject.js.

provide

The provide option should be an object or a function that returns an object. This object contains properties that can be injected into its descendants. You can use ES2015 Symbols as keys in this object, but this only works in environments with native support for Symbol and Reflect.ownKeys.

Look at the source code first:

// src/core/instance/inject.jsexport function initProvide (vm: Component) {
  const provide = vm.$options.provide  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

provide is the option to pass data downward. Here we first get the content in the provide option. If there is a provide option, pass the provide option to vm._provided and turn it into Vue instance global data.
Let’s look at the example here to make it clearer. In the following example, the data foo is passed in, and the data content is bar.

var Provider = {
  provide: {
    foo: 'bar'
  },  // ...}

inject

#inject option should be a string array or an object, the key of the object represents the name of the local binding, and the value is its key (string or Symbol ) to search among available injections.

Source code

// src/core/instance/inject.jsexport function initInjections (vm: Component) {
  const result = resolveInject(vm.$options.inject, vm)  if (result) {
    observerState.shouldConvert = false
    Object.keys(result).forEach(key => {
      defineReactive(vm, key, result[key])
    })
    observerState.shouldConvert = true
  }
}

The simplified source code can be seen. First, obtain the inject option search results through the resolveInject method. If there are search results, traverse the search results and Add setters and getters for the data in it.
Let’s take a look at resolveInject method:

export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {    // inject 是 :any 类型因为流没有智能到能够指出缓存
    const result = Object.create(null)    // 获取 inject 选项的 key 数组
    const keys = hasSymbol
      ? Reflect.ownKeys(inject).filter(key => {        /* istanbul ignore next */
        return Object.getOwnPropertyDescriptor(inject, key).enumerable
      })
      : 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 && provideKey in source._provided) {
          result[key] = source._provided[provideKey]          break
        }
        source = source.$parent
      }      if (!source) {        if (&#39;default&#39; in inject[key]) {          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === &#39;function&#39;
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== &#39;production&#39;) {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }    return result
  }
}

Get the key array of the inject option, traverse the key array, and bubble up to find out whether there is a key in the provide that matches the from in the inject option. If the attribute has the same name, this data will be passed to the result; if not, check whether the inject has a default option to set the default value or default method. If so, the default value will be returned to the result, and finally the result object will be returned.
So, inject should be written with a default value:

const Child = {
  inject: {
    foo: { default: &#39;foo&#39; }
  }
}

Or with a from search key and a default value:

const Child = {
  inject: {
    foo: {
      from: &#39;bar&#39;,      default: &#39;foo&#39;
    }
  }
}

Or set a default value for the default value Factory method:

const Child = {
  inject: {
    foo: {
      from: &#39;bar&#39;,      default: () => [1, 2, 3]
    }
  }
}

Okay, I admit that these are the three examples quoted from the official website~ But it’s good to have an idea.
I have a question here. Since from and default are actively identified in the source code, the official website says that

injection in 2.5.0+ can be configured through The default value makes it optional:

So is the following writing method still available?

var Child = {
  inject: [&#39;foo&#39;],
  created () {
    console.log(this.foo) // => "bar"
  }  // ...}

For this reason, let’s check how the 2.2.0 version of Vue is written?

export function initInjections (vm: Component) {
  const provide = vm.$options.provide  const inject: any = vm.$options.inject  if (provide) {
    vm._provided = typeof provide === &#39;function&#39;
      ? provide.call(vm)
      : provide
  }  if (inject) {    // inject is :any because flow is not smart enough to figure out cached
    // isArray here
    const isArray = Array.isArray(inject)    const keys = isArray
      ? inject
      : hasSymbol
        ? Reflect.ownKeys(inject)
        : Object.keys(inject)    for (let i = 0; i < keys.length; i++) {      const key = keys[i]      const provideKey = isArray ? key : inject[key]      let source = vm      while (source) {        if (source._provided && source._provided[provideKey]) {
          vm[key] = source._provided[provideKey]          break
        }
        source = source.$parent
      }
    }
  }
}

As you can see, in this version provide and inject are initialized together. After that, provide is passed to vm._provide. When getting the inject option, the code determines whether the inject is an array. If it is an array, it traverses the array directly, and then the code to find provide is almost the same.
So I speculate: After 2.5.0+, you can no longer use inject in array form to search for provide.

The above is the detailed content of Vue.js source code learning provide and inject. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn