下面来梳理一下父子组件的解析过程
一个组件import之后 经过render解析 并且传进_createElement方法
vnode = createComponent(tag, data, context, children);
这里的context是vm 紧接着又调用了createComponent方法
这里有两个特别重要的函数 就是 Ctor = baseCtor.extend(Ctor) 和installComponentHooks(data);
这里的baseCtor.extend 是让Ctor 继承Vue 并且返回构造器
installComponentHooks 初始化data 给data安装钩子函数 在 componentVNodeHooks 中
function createComponent (
Ctor,
data,
context,
children,
tag
) {
if (isUndef(Ctor)) {
return
}
var baseCtor = context.$options._base; //实际上就是Vue
// plain options object: turn it into a constructor
if (isObject(Ctor)) { //每个组件都有一个独立的构造器
Ctor = baseCtor.extend(Ctor);//Vue扩展Ctor 创建Sub 继承Vue 参数放进sub.options 返回Sub 构造函数
}
// if at this stage it's not a constructor or an async component factory,
// reject.
if (typeof Ctor !== 'function') {
if (process.env.NODE_ENV !== 'production') {
warn(("Invalid Component definition: " + (String(Ctor))), context);
}
return
}
// async component
var asyncFactory;
if (isUndef(Ctor.cid)) {
asyncFactory = Ctor;
Ctor = resolveAsyncComponent(asyncFactory, baseCtor);
if (Ctor === undefined) {
// return a placeholder node for async component, which is rendered
// as a comment node but preserves all the raw information for the node.
// the information will be used for async server-rendering and hydration.
return createAsyncPlaceholder(
asyncFactory,
data,
context,
children,
tag
)
}
}
data = data || {};
// resolve constructor options in case global mixins are applied after
// component constructor creation
resolveConstructorOptions(Ctor);//检查options和全局的是否冲突
// transform component v-model data into props & events
if (isDef(data.model)) {
transformModel(Ctor.options, data);
}
// extract props
var propsData = extractPropsFromVNodeData(data, Ctor, tag);
// functional component
if (isTrue(Ctor.options.functional)) {
return createFunctionalComponent(Ctor, propsData, data, context, children)
}
// extract listeners, since these needs to be treated as
// child component listeners instead of DOM listeners
var listeners = data.on;
// replace with listeners with .native modifier
// so it gets processed during parent component patch.
data.on = data.nativeOn;
if (isTrue(Ctor.options.abstract)) {
// abstract components do not keep anything
// other than props & listeners & slot
// work around flow
var slot = data.slot;
data = {};
if (slot) {
data.slot = slot;
}
}
// install component management hooks onto the placeholder node
installComponentHooks(data); //安装一些组件的钩子
// return a placeholder vnode //生成一个vnode 这和之前的vnode不一样 是一个组件vnode
var name = Ctor.options.name || tag;
var vnode = new VNode(
("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')),
data, undefined, undefined, undefined, context,
{ Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },
asyncFactory
);
//这时候的Ctor已经不是原来的tag 而是extend返回的sub构造器
return vnode
}
componentVNodeHooks代码如下
var componentVNodeHooks = {
init: function init (vnode, hydrating) {
if (
vnode.componentInstance &&
!vnode.componentInstance._isDestroyed &&
vnode.data.keepAlive
) {
// kept-alive components, treat as a patch
var mountedNode = vnode; // work around flow
componentVNodeHooks.prepatch(mountedNode, mountedNode);
} else {
var child = vnode.componentInstance = createComponentInstanceForVnode( //返回子组件vm实例
vnode, //第一次是占位vnode
activeInstance //返回的 new Vue实例
);
child.$mount(hydrating ? vnode.elm : undefined, hydrating);
}
},
这里的createComponentInstanceForVnode 把createComponent返回的vnode 和activeInstance 传入
并且返回vm实例
function createComponentInstanceForVnode (
// we know it's MountedComponentVNode but flow doesn't
vnode,
// activeInstance in lifecycle state
parent //这里的parent实际上是一个vm的实例
) {
console.log(vnode)
var options = {
_isComponent: true, //设置是不是组件
_parentVnode: vnode,
parent: parent
};
// check inline-template render functions
var inlineTemplate = vnode.data.inlineTemplate;
if (isDef(inlineTemplate)) {
options.render = inlineTemplate.render;
options.staticRenderFns = inlineTemplate.staticRenderFns;
}//componentOptions是VNode 构造函数的第七个参数 在createComponent里返回的vnode
return new vnode.componentOptions.Ctor(options) //这个ctor其实就是一个组件的构造器Sub
//这里因为是实例化 就会执行构造器sub的 构造函数中的init init是继承自Vue
}
注意这个new vnode.componentOptions.Ctor 就会激活构造器sub的 init方法 由于init方法继承自Vue 所以就会 执行 Vue.prototype._init 里的initInternalComponent 设置父子组件关系
然后再componentVNodeHooks 中的init方法中手动调用$mount 实现update 并且在update中的createElm函数只要一遇到组件 就会调用createComponent 实现深度遍历 最后把最里面的子组件渲染完成 依次向上插入父组件的 最后在insert在最外面