Merge configuration
Through source code analysis section before we know, new Vue
a process usually scene 2, is an external active call our code new Vue(options)
of a instantiate Vue
an object; the other components of the process is that we analyzed on an inside by new Vue(options)
instantiating Subassembly.
In either scenario, the instance will execute _init(options)
the method, it will first perform a merge options
logic-related code src/core/instance/init.js
in:
Vue.prototype._init = function (options?: Object) {
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {
},
vm
)
}
// ...
}
We can see different scenarios for options
consolidation logic is not the same, and the incoming options
values also have a very big difference, then I will introduce separate two kinds of scenes options
merge process.
To be more intuitive, we can give a simple example:
import Vue from 'vue'
let childComp = {
template: '<div>{
{msg}}</div>',
created() {
console.log('child created')
},
mounted() {
console.log('child mounted')
},
data() {
return {
msg: 'Hello Vue'
}
}
}
Vue.mixin({
created() {
console.log('parent created')
}
})
let app = new Vue({
el: '#app',
render: h => h(childComp)
})
External call scene
When the execution new Vue
time, the execution this._init(options)
time will merge logic to perform the following options
:
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {
},
vm
)
Here by calling mergeOptions
to merge method, which is actually the resolveConstructorOptions(vm.constructor)
return value and options
do merge, resolveConstructorOptions
the realization is not considered, in our scenario, it is simple returns vm.constructor.options
, the equivalent Vue.options
, then the value of what is it, in fact, in initGlobalAPI(Vue)
the when defining this value, the code src/core/global-api/index.js
in:
export function initGlobalAPI (Vue: GlobalAPI) {
// ...
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)
// ...
}
First, by Vue.options = Object.create(null)
creating an empty object, and then traverse ASSET_TYPES
, ASSET_TYPES
the definition src/shared/constants.js
of:
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
Therefore traversal above ASSET_TYPES
code corresponds to:
Vue.options.components = {
}
Vue.options.directives = {
}
Vue.options.filters = {
}
Then it was executed Vue.options._base = Vue
, and its function was introduced when we instantiated the sub-components in the previous section.
Finally extend(Vue.options.components, builtInComponents)
extended to a number of built-in components Vue.options.components
on Vue
the built-in components are currently <keep-alive>
, <transition>
and <transition-group>
components, which is why we use other components <keep-alive>
reason components do not require registration, this child follow us <keep-alive>
assembly time will tell in detail.
So back to mergeOptions
this function, which is defined src/core/util/options.js
in:
/**
* Merge two option objects into a new one.
* Core utility used in both instantiation and inheritance.
*/
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
const extendsFrom = child.extends
if (extendsFrom) {
parent = mergeOptions(parent, extendsFrom, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
const options = {
}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
mergeOptions
The main function is to parent
and child
these two objects, according to some consolidation strategy, combined into a new object and return. More central steps, the first recursive extends
and mixins
incorporated into parent
, and then traverse the parent
call mergeField
, then traverse child
, if key
not parent
on their own property, then call mergeField
.
Here is an interesting mergeField
function, which is different key
with different consolidation strategy. For example, for the life cycle function, its merge strategy is like this:
function mergeHook (
parentVal: ?Array<Function>,
childVal: ?Function | ?Array<Function>
): ?Array<Function> {
return childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
}
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
Which LIFECYCLE_HOOKS
is defined in src/shared/constants.js
the:
export const LIFECYCLE_HOOKS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroy',
'destroyed',
'activated',
'deactivated',
'errorCaptured'
]
This defines Vue.js all hook function name, so the hook function, their strategies are merging mergeHook
function. Implementation of this function is also very interesting, with a multi-layer three yuan operator, if the logic is not present childVal
, returns parentVal
; otherwise, then determines whether there is parentVal
, if there is childVal
added to parentVal
return the new array, otherwise childVal
the array. So back mergeOptions
functions, once parent
and child
are defined the same hook function, then they will merge two hook function into an array.