Ctor
前面講vnode生成都是通過判斷tag是否爲string類型時的過程,當我們使用組件時情況就不一樣了,這裏主要是通過createComponent方法實現的。
首先照常先看函數簽名,由以下五個:
Ctor: Class<Component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ?Array<VNode>,
tag?: string
首先是一個傳入的tag,不是字符串,而是一個構造函數,data即VNodeData, context即vm實例,首先要把vm.$options._base賦值給新的變量:
const baseCtor = context.$options._base
這裏baseCtor即爲Vue,下一步就是調用Vue.extend方法,轉到Vue.extend之中,裏面第一個重要邏輯就是對所謂的tag進行校驗
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
這裏也會看到常見的錯誤:
if (!new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeRegExp.source}]*$`).test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'should conform to valid custom element name in html5 specification.'
)
}
if (isBuiltInTag(name) || config.isReservedTag(name)) {
warn(
'Do not use built-in or reserved HTML elements as component ' +
'id: ' + name
)
}
再接着會創建一個子的構造函數去繼承原構造函數,並進行初始化等操作優化,最後返回的也是子類構造函數;同時也做了一層緩存,節約了多次調用createComponent時不必要的消耗,通過這兩種方式返回新的構造器。
接着讓我們再返回createCompoent。
createCompoent
通過剛剛的操作我們拿到了新的構造器,之後會有一些v-model,異步組件之類的邏輯,我們先忽略,直到走到 installComponentHooks, 這裏是用來掛載組件的一些鉤子函數。
最後我們會調用:
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ""}`,
data,
undefined,
undefined,
undefined,
context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
);
這裏對照vnode的構造函數的實現,我們可以發現一些端倪,首先就是children,element,text都是undefined的,這個原因也會後面講到,接着還會傳遞一個componentOptions,在這個裏面反而傳遞了children,tag等東西。
最後返回vnode即可。