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.
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
.
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 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 ('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 } }
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: 'foo' } } }
Or with a from search key and a default value:
const Child = { inject: { foo: { from: 'bar', default: 'foo' } } }
Or set a default value for the default value Factory method:
const Child = { inject: { foo: { from: 'bar', 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: ['foo'], 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 === 'function' ? 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!