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
functioncreateComponent(Ctor,data,context,// 限制作用域children,tag){if(isUndef(Ctor)){return}// Vue 的根构造函数varbaseCtor=context.$options._base;// plain options object: turn it into a constructor// 即局部组件的注册if(isObject(Ctor)){Ctor=baseCtor.extend(Ctor);}// if at this stage it's not a constructor or an async component factory,// reject.if(typeofCtor!=='function'){{warn(("Invalid Component definition: "+(String(Ctor))),context);}return}
...
// return a placeholder vnodevarname=Ctor.options.name||tag;// 子vnode的id使用vue-component及对应的id来进行标识varvnode=newVNode(("vue-component-"+(Ctor.cid)+(name ? ("-"+name) : '')),data,undefined,undefined,undefined,context,// 这个参数为 componentOptions,即用于创建 componentInstance 实例{Ctor: Ctor,propsData: propsData,listeners: listeners,tag: tag,children: children},asyncFactory);
...
returnvnode}
组件实例化的 options 合并策略
options 的合并应该说是 Vue 生命周期当中非常重要的一环。Vue 的组件实例化都是通过调用对应的构造函数完成相关实例化的工作。而在组件真正由 VNode 实例化成 vue 组件之前有关组件的相关定义都是挂载在组件的构造函数的 options 属性上。
通过new Vue实例化的组件 options 合并
我们定义一个全局的组件:
Vue.component
方法内部实际上是调用的Vue.extend
方法,在这个方法内部完成这个组件的 options 的初始化的工作:这个方法接收 extendOptions 参数即为组件的定义,然后在方法内部调用 mergeOptions 方法(这个方法内部到底做了哪些工作后面会讲)去完成对于根 Vue 构造函数上的 options 属性的合并,并将合并后的 options 属性赋值给子组件的构造函数 options 属性。这样便完成了组件 options 属性初始化的工作。通过
Vue.component
方法定义的全局组件,会在根 Vue 实例的 options 属性上挂载这个全局组件的构造函数。这也是为什么定义的全局组件如果要在组件当中使用时,可直接在模板当中进行书写,而不用在配置项里面去注册声明(这块的解释后面会讲)。当组件真正进行实例化(new Vue)的阶段时,这里要分情况讨论了,首先让我们来看下根组件进行实例化:
这里通过判断
options._isComponent
属性是进行内部子组件的实例化还是根组件的实例化。当是根组件的实例化的时候进入else
的流程,调用mergeOptions
方法完成 options 合并之前,首先调用 resolveConstructorOptions 方法获取构造函数上的 options 配置(这里不讨论单独通过 Vue.extend 方法完成继承的情况)。因为没有继承关系,所以这个方法最终还是返回这个构造函数上的 options 配置。第二个参数为外部传入的 options 配置,第三个参数为这个 vm 实例。最终将得到的 options 配置赋值给 vm 实例的 $options 属性上,并由这个 $options 属性去接着完成 vm 实例化。接下来我们看下
mergeOptions
方法内部到底做了些什么工作。首先遍历构造函数上的 options 的 key,执行 mergeField 方法,然后遍历实例化组件的过程中传入的 options 的 key,并执行 mergeField 方法。其中 mergeField 方法内部就是关于 options 当中各个字段预设的合并策略。
会调用 defaultStrat 方法进行默认的合并策略:
关于 el 和 propsData 属性的合并还有一些限制就是:只允许通过
new Vue
(Vue 为根构造函数)这种形式去实例化一个 Vue 的实例。子组件的实例化过程当中是不允许传入这 2 个字段的。在实例化非组件实例时,并没有直接返回合并后的 data 的值,返回而是一个 mergedInstanceDataFn 函数:
当实例化的阶段进入 initState 时,才会实际执行这个 mergedInstanceDataFn 函数,且是在传入的 vm 实例的作用域下执行的。然后通过 mergeData 方法将需要继承的 data 和 实例化传入的 data 进行合并。
会将每个生命周期的钩子函数都置为一个数组。
有关这三者的合并策略是通过原型继承的方式去实现的。
如果实例没有 watch,那么直接通过原型继承的方式返回构造函数上的 watch,如果构造函数上没有 watch,且实例有 watch,那么就直接返回实例的 watch。当两者都有的时候,那么需要将每个 key 对应的 watch 转化为数组,且先执行被继承来的 watch 函数。
有关这四者的合并策略即:如果没有需要继承值,那么直接返回实例传入的值,否则通过覆盖的形式去完成值的合并的过程。
在 mergeOptions 方法里面有个地方没有提到,就是关于 mixins 混入。mixins 可以算做我们去实现 HOC 的一种常用的手段。一个组件可以接受一个 mixins 数组。mergeOptions 方法首先会遍历 mixins 数组,依次将每个 mixin 混入到构造函数的 options 属性上,然后再完成和实例中传入的 options 的合并。在 Vue 的根构造函数上也提供了一个全局的 mixin 的方法
Vue.mixin
。这个全局的 mixin 方法会在 Vue 的根构造函数的 options 配置选项上混入你所传入的配置选项。组件 options 合并
组件在创建 VNode 的过程中:
创建 VNode 之前,首先通过
baseCtor.extend(Ctor)
方法获取这个局部组件的构造函数,在这个方法内部会完成构造函数的 options 和这个局部组件配置 options 选项的合并工作,并将 options 属性挂载至这个局部组件的构造函数上。当进入到 VNode 实例化的过程中:这个时候会调用
Vue.prototype._init
方法开始子组件的实例化:这时便会调用
initInternalComponent
方法去完成子组件的 options 合并:在这个方法内部,通过原型继承的方法完成子组件实例的 $options 配置的初始化的过程。同时根据传入的 options 配置项去完成 VNode 在最终渲染前的配置工作。
总结
在 Vue 进行实例化之前,组件 options 配置选项的继承始终是通过实例的构造函数去完成,构造函数的 options 合并是完成组件实例化的先行任务。
其中组件的实例化和直接通过
new Vue
去完成的实例化的 options 合并过程有有些区别。组件的实例化之前,会首先在 Vue.extend 方法内部调用mergeOptions
去完成构造函数 options 的合并,开始实例化后(Vue.prototype._init
方法),调用initInternalComponent
方法,通过继承的方法初始化实例的 $options 选项。而通过
new Vue
的方式直接完成实例化时,是在实例化开始后,调用mergeOptions
方法去初始化实例的 $options 配置选项。The text was updated successfully, but these errors were encountered: