我们之前了解了Vue首次渲染标签和组件的流程,现在我们来总结下渲染过程中的配置合并。
首先是在_init
中配置的合并,当首次渲染的时候会执行以下代码
Vue.prototype._init = function (options?: Object) {
...
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
)
}
...
}
非组件的时候通过mergeOptions
来合并配置,resolveConstructorOptions
函数里做了一些合并父子构造器的操作,当第一次执行时,这里可以看做是Vue.options
,那么Vue.options
又是什么,其实在初始化的时候已经为我们配置了一些参数。
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)
// ...
}
在initGlobalAPI
中,先是创建了一个空对象,然后循环遍历ASSET_TYPES
来设置配置。
ASSET_TYPES
定义在vue/src/shared/constants.js
中
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
当遍历完之后,Vue.options
有了三个键值对。
Vue.options = {
components: {},
directives: {},
filters: {}
}
之后又设置了Vue.options._base = Vue
。
最后通过extend
方法将内置的组件添加到Vue.options.components
中,像<keep-alive>``<transition>
都是内置的组件,所以我们可以直接写在模板中。
我们知道了Vue.options
后,来看mergeOptions
的操作。
/**
* 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)
// Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, 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
}
这里主要根据一定的策略对传入的配置进行了合并操作。
当我们面对的是组件场景的时候,我们在创建子组件构造器时就进行了配置合并。
/**
* Class inheritance
*/
Vue.extend = function (extendOptions: Object): Function {
// ...
Sub.options = mergeOptions(
Super.options,
extendOptions
)
// ...
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// ...
return Sub
}
extendOptions
是子组件传入的配置,Super.options
是父组件构造器的配置。
在子组件实例化时,会执行_init
方法,这个时候合并配置流程中会执行initInternalComponent
方法
export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
const opts = vm.$options = Object.create(vm.constructor.options)
// doing this because it's faster than dynamic enumeration.
const parentVnode = options._parentVnode
opts.parent = options.parent
opts._parentVnode = parentVnode
const vnodeComponentOptions = parentVnode.componentOptions
opts.propsData = vnodeComponentOptions.propsData
opts._parentListeners = vnodeComponentOptions.listeners
opts._renderChildren = vnodeComponentOptions.children
opts._componentTag = vnodeComponentOptions.tag
if (options.render) {
opts.render = options.render
opts.staticRenderFns = options.staticRenderFns
}
}
在这个方法中只是对之前的子组件构造器进行了赋值操作。如
opts.parent = options.parent
opts._parentVnode = parentVnode
等。
这就是简单的配置合并的过程。