# createComponent
# createComponent
- 创建组件的VNode
export function createComponent(
Ctor: Class<component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ? Array<VNode>,
tag?: string
): VNode | Array<VNode> | void {
// 如果组件构造函数不存在, 直接结束
if (isUnDef(Ctor)) return
// Vue.extend
const baseCtor = context.$options._base
// 当 Ctor 为配置对象时,通过 Vue.extend 将其转为构造函数
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor)
}
// 如果到这个为止,Ctor仍然不是一个函数,则表达式是一个无效的组件定义
if (typeof Ctor !== 'function') return
// 异步组件
let asyncFactroy
if (isUndef(Ctor.cid)) {
asyncFactroy = Ctor
Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
if (Ctor === undefined) {
// 为异步组件返回一个占位符节点
return createAsyncPlaceholder(
asyncFactory,
data,
context,
children,
tag
)
}
}
// 节点的属性 JSON 字符串
data = data || {}
// 这里其实就是组件做选项合并的地方 下方
resolveConstructorOptions(Ctor)
// 将组件的 v-model 信息 转换为 data.attrs
if (isDef(data.model)) {
transformModel(Ctor.options, data)
}
// 从 attrs 中提取 props 数据, 得到 propsData 对象
const propsData = extractPropsFromVNodeData(data, Ctor, tag)
// 函数式组件
if (isTrue(Ctor.options.functional)) {
return createFunctionalComponent(Ctor, propsData, data, context, children)
}
// 获取事件监听器对象 data.on
const listeners = data.on
data.on = data.nativeOn
if (isTrue(Ctor.options.abstract)) {
// 如果是抽象组件,则保留props,listeners和slot
const slot = data.slot
data = {}
if (slot) {
data.slot = slot
}
}
// 在组件的 data 对象上设置 hook 对象
// hook 对象增加四个属性, init、prepatch、insert、destroy
// init 会为 VNode.conponentInstance 创建 对应 vue 实例
installComponentHooks(data)
const name = Ctor.options.name || tag
const vnode = new VNode(
`vue-component-${Ctor.id}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
if (__WEEX__ && isRecyclableComponent(vnode)) {
return renderRecyclableComponentTemplate(vnode)
}
return vnode
}
resolveConstructorOptions 与 resloveModifiedOptions
- 应该是处理 Vue.extend 的 Ctor
export function resolveConstructorOptions(Ctor: Class<Component>) {
// 从实例构造函数上获取选项
let options = Ctor.options
if (Ctor.super) {
// 递归处理所有的父级
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions
if (superOptions !== cachedSuperOptions) {
// 基类的配置项发生了更改
Ctor.superOptions = superOptions
const modifiedOptions = resolveModifiedOptions(Ctor)
if (modifiedOptions) {
// 将更改的选项合并
extend(Ctor.extendOptions, modifiedOptions)
}
options = Ctor.options = mergeOption(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}
function resolveModifiedOptions (Ctor: Class<Component>)?: Object {
let modified
const latest = Cotr.options
const sealed = Ctor.sealeOptions
// 将 latest 与 sealed 中不相同的放入 modified 返回
for (const key in latest) {
if (last[key] !== sealed[key]) {
if (!modified) modified = {}
modified[key] = latest[key]
}
}
return modified
}
transformModel
- 将组件的 v-model 转换为 data.attrs 对象属性和 data.on 对象上的事件
function transformModel(options, data) {
const prop = (options.model && options.model.prop) || 'value'
const event = (options.model && options.model.event) || 'input'
; (data.attrs || (data.attrs = {}))[prop] = data.model.value
const on = data.on || (data.on = {})
// 获取对应的 event 已存在的事件
const existing = on[event]
const callback = data.model.callback
if (isDef(existing)) {
if (
Array.isArray(existing)
? existing.indexOf(callback) === -1
: existing !== callback
) {
// 如果是个数组 合并回调函数
on[event] = [callback].concat(existing)
} else {
on[event] = callback
}
}
}
extractPropsFromVNodeData
// 处理 attrs 与 props
export function extractPropsFromVNodeData (
data: VNodeData,
Ctor: Class<Component>,
tag?: tag
): ?Object {
const propOptions = Ctor.options.props
// 未定义 props 直接返回
if (isUndef(propOptions)) return
const res = {}
const { attrs, props } = data
if (isDef(attrs) || isDef(props)) {
for (const key in propOptions) {
// 将小驼峰的 key 转换为 连字符 形式
const altKey = hyphenate(key)
checkProp(res, props, key, altKey, true) ||
checkProp(res, attrs, key, altKey, false)
}
}
return res
}
function checkProp (
res: Object,
hash: ?Object,
key: string,
altKey: string,
preserve: boolean
): boolean {
if (isDef(hash)) {
if (hasOwn(hash, key)) {
res[key] = has[key]
if (!preserve) delete hash[key]
return true
} else if (hasOwn(hash, altKey)) {
res[key] = hash(altKey)
if (!preserve) delete hash[altKey]
return true
}
}
return false
}
# createFunctionalComponent
← Render-help patch →