在看createElement方法时我们了解到_createElement 方法里面对 tag 的判断,
如果是一个常规的 html 标签,比如 div,
会实例化一个 VNode 节点,
否则使用 createComponent 创建一个组件 VNode。
比如这种,拿到App进行的处理,
会使用 createComponent 创建一个组件 VNode
import Vue from 'vue'
import App from './App.vue'
var app = new Vue({
el: '#app',
render: h => h(App)
})
const vm = new Vue({
render: c=>{
return (
c('div', {}, [['1', '2', '3']])
)
}
})
export function createComponent (
Ctor: Class<Component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ?Array<VNode>,
tag?: string
): VNode | Array<VNode> | void {
...
const baseCtor = context.$options._base
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor)
}
...
let asyncFactory
... 异步相关处理
data = data || {}
resolveConstructorOptions(Ctor)
if (isDef(data.model)) {
transformModel(Ctor.options, data)
}
const propsData = extractPropsFromVNodeData(data, Ctor, tag)
...函数式组件
...监听器、参数等处理
installComponentHooks(data)
const name = Ctor.options.name || tag
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
...__WEEX__相关
return vnode
}
组件渲染主要3个步骤:
生成构造函数,安装组件钩子函数和实例化 vnode
export default {
。。。
}
export 的是一个对象,所以会执行 baseCtor.extend(Ctor),
baseCtor 实际上就是 Vue,定义在最开始初始化 Vue 的阶段,
在 src/core/global-api/index.js 的initGlobalAPI有一行:
---
Vue.options._base = Vue
---
在 src/core/instance/init.js 的 Vue 原型上 _init 函数中:
---
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
---
mergeOptions的功能是把 Vue 构造函数的 options
和用户传入的 options 合并到 vm.$options 上
Vue.extend 定义在 src/core/global-api/extend.js 的initExtend中
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
...
const Sub = function VueComponent (options) {
this._init(options)
}
...继承处理
cachedCtors[SuperId] = Sub
return Sub
}
Vue.extend 作用是构造 Vue 的子类
然后对 Sub 这个对象本身扩展了一些属性
实例化 Sub,就会执行 this._init 就回到了 Vue 实例的初始化
installComponentHooks 是把 componentVNodeHooks 的钩子函数
合并到 data.hook 中,patch 的时候执行相关的钩子函数
const componentVNodeHooks = {...}
function installComponentHooks (data: VNodeData) {
const hooks = data.hook || (data.hook = {})
for (let i = 0; i < hooksToMerge.length; i++) {
const key = hooksToMerge[i]
const existing = hooks[key]
const toMerge = componentVNodeHooks[key]
if (existing !== toMerge && !(existing && existing._merged)) {
hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge
}
}
}
function mergeHook (f1: any, f2: any): Function {
const merged = (a, b) => {
f1(a, b)
f2(a, b)
}
merged._merged = true
return merged
}
合并过程中,某个钩子已经存在 data.hook 中,通过执行 mergeHook 合并
执行的时候,依次执行这两个函数就行了
最后通过 new VNode 实例化一个 vnode 并返回。
值得注意的是和普通 vnode 相比组件的 vnode 是没有 children 的