You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
exportfunctionresolveConstructorOptions(Ctor: Class<Component>){letoptions=Ctor.optionsif(Ctor.super){constsuperOptions=resolveConstructorOptions(Ctor.super)constcachedSuperOptions=Ctor.superOptionsif(superOptions!==cachedSuperOptions){// super option changed,// need to resolve new options.Ctor.superOptions=superOptions// check if there are any late-modified/attached options (#4976)constmodifiedOptions=resolveModifiedOptions(Ctor)// update base extend optionsif(modifiedOptions){extend(Ctor.extendOptions,modifiedOptions)}options=Ctor.options=mergeOptions(superOptions,Ctor.extendOptions)if(options.name){options.components[options.name]=Ctor}}}returnoptions}
strats.data=function(parentVal: any,childVal: any,vm?: Component): ?Function{if(!vm){if(childVal&&typeofchildVal!=='function'){process.env.NODE_ENV!=='production'&&warn('The "data" option should be a function '+'that returns a per-instance value in component '+'definitions.',vm)returnparentVal}returnmergeDataOrFn(parentVal,childVal)}returnmergeDataOrFn(parentVal, childVal, vm)}
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=Vueextend(Vue.options.components,builtInComponents)initUse(Vue)initMixin(Vue)initExtend(Vue)initAssetRegisters(Vue)// ASSET_TYPES 定义在constance.js里exportconstASSET_TYPES=['component','directive','filter']
这是个合并全局options的过程,同样的把全局传入的mixin对象,通过 mergeOptions 把传入的对象,也混入到 Vue 的 options 上,这些就是在执行 new Vue 的时候进行的合并。
另一个合并在子组件在初始化的时候,先回忆一下组件的构造函数过程:
/** * 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.optionsSub.extendOptions=extendOptionsSub.sealedOptions=extend({},Sub.options)// ...returnSub}
exportfunctioninitInternalComponent(vm: Component,options: InternalComponentOptions){constopts=vm.$options=Object.create(vm.constructor.options)// doing this because it's faster than dynamic enumeration.constparentVnode=options._parentVnodeopts.parent=options.parentopts._parentVnode=parentVnodeconstvnodeComponentOptions=parentVnode.componentOptionsopts.propsData=vnodeComponentOptions.propsDataopts._parentListeners=vnodeComponentOptions.listenersopts._renderChildren=vnodeComponentOptions.childrenopts._componentTag=vnodeComponentOptions.tagif(options.render){opts.render=options.renderopts.staticRenderFns=options.staticRenderFns}}
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)}
此时就会执行 initInternalComponent:
exportfunctioninitInternalComponent(vm: Component,options: InternalComponentOptions){constopts=vm.$options=Object.create(vm.constructor.options)// doing this because it's faster than dynamic enumeration.constparentVnode=options._parentVnodeopts.parent=options.parentopts._parentVnode=parentVnodeconstvnodeComponentOptions=parentVnode.componentOptionsopts.propsData=vnodeComponentOptions.propsDataopts._parentListeners=vnodeComponentOptions.listenersopts._renderChildren=vnodeComponentOptions.childrenopts._componentTag=vnodeComponentOptions.tagif(options.render){opts.render=options.renderopts.staticRenderFns=options.staticRenderFns}}
合并配置(mergeOptions)在两个地方出现,一个是代码主动调用new Vue的时候,一个是创建子组件调用 new Vue的时候,它们都会执行
_init(options)
方法,:来看下
_init
的逻辑:可以看到两种方式是不一样的,创建子组件的时候用
initInternalComponent
, 另一个用mergeOptions
,先来看下第二种:调用了
resolveConstructorOptions(vm.constructor),
方法:这里传入的Ctor参数是Vue,所以它没有 super,最后返回了 Vue 的 options。
所以在调用
mergeOptions
的时候,传入的第一个参数就是大 Vue 的 options,第二个 options 就是在代码中写new Vue({})
的时候传入的参数(比如render,el等),它们两个通过mergeOptions
合并到了一起,赋值给vm.$options
,来看下这个mergeOptions
的逻辑,它在src/core/util/options.js
:它其实就是把参数
parent
和参数child
合并,先递归把extends
和mixins
合并到parent
上,然后遍历parent
,调用mergeField
,然后再遍历child
,如果key
不在parent
上,就调用mergeField
。在
mergeField
中就是调用strats
方法,根据传入的key
的不同得到不同的strat
,如果没有就是defaultStrat
:可见
defaultStrat
优先是parent
,然后是child
(一个简单的合并策略)。接着上面的strats
,它其实就是定义了很多合并策略,strats
最开始的定义是:optionMergeStrategies
的定义是一个空对象:Object.create(null)
,也就是说这个strats
主要是用来扩展的,后面接着在strats
上扩展了很多属性,比如data:又比如
component
,filter
:这里主要说下生命周期是如何合并的:
可以看到
parentVal
和childVal
可以传Function
或者Array<Function>
,返回了Array<Function>
,也就是返回了一个Function
类型的数组,返回值逻辑:知道了
mergeOptions
的逻辑之后,再看下它的第一个参数:resolveConstructorOptions
,这个返回 Vue 的 options,这个 Vue 的 options 定义在src/core/global-api/index.js
:在 Vue 初始化的时候定义了一个空对象
options
,ASSET_TYPES
也都扩展到这个options
里面,接着用builtInComponents
扩展了一些内置组件(比如transition,keepAlive),把它们都添加到_init
中的vm.$options
上。而在mixin模块:
这是个合并全局options的过程,同样的把全局传入的mixin对象,通过
mergeOptions
把传入的对象,也混入到 Vue 的 options 上,这些就是在执行 new Vue 的时候进行的合并。另一个合并在子组件在初始化的时候,先回忆一下组件的构造函数过程:
这里的
extendOptions
对应的就是前面定义的组件对象,它会和Vue.options
合并到Sub.opitons
中。接着回忆一下子组件的初始化过程:
vnode.componentOptions.Ctor
指向的是Vue.extend
的返回值Sub
,所以在执行它的时候,会接着执行子组件的this._init(options)
。此时合并过程走到了
initInternalComponent
方法:注意调用它的时候,传入的
vm
是子组件实例,所以里面的vm.constructor.options
就是子组件构造器上的options
,子组件构造器是在Vue.extend
的时候拿到的options
,所以这里的vm.constructor
就是子组件构造函数的Sub
,相当于vm.$options = Object.create(Sub.options)
,而这里用了Object.create
方式创建,所以相当于:vm.$options.__proto__
= 子组件实例合并的options。接着又把实例化子组件传入的子组件父 VNode 实例
parentVnode
、子组件的父 Vue 实例parent
保存到vm.$options
中,另外还保留了parentVnode
配置中的如propsData
等其它的属性。举个例子把上面的过程捋一遍(重点看多个created是如何合并的):
在
new Vue
之前,会先执行Vue.mixin
,也就是合并全局的options
,也就是Vue.options
,所以第一次走到mergeOptions
的时候,参数parent
上没有的,参数child
上是created
,接着进行mergeHook
的时候,参数parenVal
是没有的(因为new Vue({})上没有created),参数childVal
就是Vue.mixin
上的created
,然后返回一个[created(){}]
,此时执行mergeField
的options
上多了一个created
属性,值是[created(){}]
,到此Vue.mixin
的合并结束。接着执行
new Vue
逻辑,在执行_init
的时候,会执行:此时
resolveConstructorOptions(vm.constructor)
返回的就是大 Vue 的 options,接着执行到mergeOptions
的时候,parent
就是大 Vue 的 options,此时的parent
多了一个created
,这个created
是刚才Vue.mixin
添加进去的,而此时的child
就是#app
了,这一步到最后会赋值给vm.$options
。再往后,又会继续走
mergeOptions
,这次是Vue.extend
创建子组件构造器的时候执行的:因为子组件构造器是继承大 Vue 的,所以这里的
Super.options
就是大 Vue 的 options,把它和子组件自定义的对象(例子中的childComp),也就是对子组件定义的配置进行合并,所以在执行到这一步的mergeOptions
的时候,该方法的参数parent
就是前面合并之后的大 Vue 的 options,而参数child
就是子组件定义的配置(例子中的childComp),注意:参数child
上有个created
,而参数parent
上也有created
,接着在执行到mergeHook
的时候,就会执行:此时会走到合并父子,也就是代码中的
parentVak.concat(childVal)
,也就是 先父后子。然后
mergeField
返回的就是通过mergeOptons
合并之后的options
,也就是子组件构造器的options
,也就是Sub.options
,此时里面的created
属性就是[created(){}, created(){}]
。到这里
new Vue
的合并就结束了,后面在执行子组件的初始化的时候,会执行this._init
:然后就再次执行到:
此时就会执行
initInternalComponent
:这里的
vm.constructor.options
就是刚才合并了的options
,然后把它通过Object.create
创建,并赋值给vm.$options
,这样的话vm.$options
就会有一个__proto__
属性,它的值就是options
的内容。接着把 子组件的父vnode(options._parentVnode) 和 子组件的父Vue实例(options.parent),都赋值到
vm.$options
上,然后把组件创建时候的一些配置也赋值给vm.$options
,最终合并出来的vm.$options
就有了:总结
对于
new Vue
是通过mergeOption
合并的,对于组件是通过initInternalComponent
合并的,而initInternalComponent
合并比较简单,所以它的合并更快。The text was updated successfully, but these errors were encountered: