下面來梳理一下父子組件的解析過程
一個組件import之后 經(jīng)過render解析 并且傳進_createElement方法
vnode = createComponent(tag, data, context, children);
這里的context是vm 緊接著又調(diào)用了createComponent方法
這里有兩個特別重要的函數(shù) 就是 Ctor = baseCtor.extend(Ctor) 和installComponentHooks(data);
這里的baseCtor.extend 是讓Ctor 繼承Vue 并且返回構(gòu)造器
installComponentHooks 初始化data 給data安裝鉤子函數(shù) 在 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)) { //每個組件都有一個獨立的構(gòu)造器
Ctor = baseCtor.extend(Ctor);//Vue擴展Ctor 創(chuàng)建Sub 繼承Vue 參數(shù)放進sub.options 返回Sub 構(gòu)造函數(shù)
}
// 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已經(jīng)不是原來的tag 而是extend返回的sub構(gòu)造器
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, //設(shè)置是不是組件
_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 構(gòu)造函數(shù)的第七個參數(shù) 在createComponent里返回的vnode
return new vnode.componentOptions.Ctor(options) //這個ctor其實就是一個組件的構(gòu)造器Sub
//這里因為是實例化 就會執(zhí)行構(gòu)造器sub的 構(gòu)造函數(shù)中的init init是繼承自Vue
}
注意這個new vnode.componentOptions.Ctor 就會激活構(gòu)造器sub的 init方法 由于init方法繼承自Vue 所以就會 執(zhí)行 Vue.prototype._init 里的initInternalComponent 設(shè)置父子組件關(guān)系
然后再componentVNodeHooks 中的init方法中手動調(diào)用$mount 實現(xiàn)update 并且在update中的createElm函數(shù)只要一遇到組件 就會調(diào)用createComponent 實現(xiàn)深度遍歷 最后把最里面的子組件渲染完成 依次向上插入父組件的 最后在insert在最外面