前言
在Vue的渲染过程中都会先将代码中的标签转化成render函数,然后通过render函数中的creatElement来创建Vnode。那么,在Vue中createElement又是怎么定义的?
createElement()方法
在Vue.js开发中可以利用createElement方法创建VNode,createElement()方法定义在Vue源码的 src/core/vnode/create-element.js 中
export function createElement (
context: Component, // vm实例
tag: any, // vnode的标签
data: any, // vnode的数据
children: any, // vnode子节点
normalizationType: any,
alwaysNormalize: boolean
): VNode | Array<VNode> {
// 参数的重载
// 如果data是数组或data是string、number、symbol、boolean
if (Array.isArray(data) || isPrimitive(data)) {
// 令nomalizationType = children
normalizationType = children
// 令children等于data
children = data
// 令data为undefined
data = undefined
}
// 判断alwaysNormalize是否为布尔值true
if (isTrue(alwaysNormalize)) {
// 令normalizationType = 1
normalizationType = ALWAYS_NORMALIZE
}
// 调用_context传入context、tag、data、children、normalizationType
// 即定义createElement这个方法是参数为6个
// 但实际上调用的是_createElement,即createElement是对_createElement进行包装
// 并且调用_createElement实际用到只有5个参数
// context vm实例、tag vnode的标签、data vnode的数据、normalizationType数值
return _createElement(context, tag, data, children, normalizationType)
}
可以看到在createElement()方法中只是对传入的参数进行了一些操作,然后调用了_createElement()方法,即在vnode创建的过程中,几乎都是_createElement()方法完成的,而_createElement()方法与createElement()方法一样定义在这个文件中(下面只摘出部分_createElement()源码)
export function _createElement (
context: Component, // vm实例
tag?: string | Class<Component> | Function | Object, // Dom标签
data?: VNodeData, // vnode数据
children?: any,
normalizationType?: number
): VNode | Array<VNode> {
// isDef判断data是否已定义且不为空
// isDef(data.__ob___)判断data是否是具备观察者属性
if (isDef(data) && isDef((data: any).__ob__)) {
// 如果是开发环境
// 抛出错误提示:不能使用已被观察的数据对象作为虚拟节点的数据
// 通常都是创建新的vnode的data对象在每一个渲染函数中
process.env.NODE_ENV !== 'production' && warn(
`Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
'Always create fresh vnode data objects in each render!',
context
)
// 创建一个空的虚拟节点返回
return createEmptyVNode()
}
// object syntax in v-bind
// 判断是否带有v-bind语法
if (isDef(data) && isDef(data.is)) {
// 令tag等于data.is
tag = data.is
}
// 如果vnode标签不存在
if (!tag) {
// 返回一个空格vnode
return createEmptyVNode()
}
// 如果此时是开发环境且data是已定义不为null且data.key是原始的类型
if (process.env.NODE_ENV !== 'production' &&
isDef(data) && isDef(data.key) && !isPrimitive(data.key)
) {
....
}
// 判断vnode的children是否为数组,且第一个元素是否为function
if (Array.isArray(children) &&
typeof children[0] === 'function'
) {
data = data || {}
data.scopedSlots = { default: children[0] }
children.length = 0
}
// 如果normalizationType为ALWAYS_NORMALIZE
if (normalizationType === ALWAYS_NORMALIZE) {
// 改造children数组
children = normalizeChildren(children)
} else if (normalizationType === SIMPLE_NORMALIZE) {
// 改造children数组
children = simpleNormalizeChildren(children)
}
let vnode, ns
// 如果tag是string
if (typeof tag === 'string') {
let Ctor
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
if (config.isReservedTag(tag)) {
...
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined, undefined, context
)
} else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
vnode = createComponent(Ctor, data, context, children, tag)
} else {
vnode = new VNode(
tag, data, children,
undefined, undefined, context
)
}
} else {
vnode = createComponent(tag, data, context, children)
}
if (Array.isArray(vnode)) {
return vnode
} else if (isDef(vnode)) {
if (isDef(ns)) applyNS(vnode, ns)
if (isDef(data)) registerDeepBindings(data)
return vnode
} else {
return createEmptyVNode()
}
}
_cleamElement()方法接受5个参数:context(vm实例),tag( 标签名|组件|函数)、data(vnode数据)、children 、 nomalizationType。在这个方法中创建Vnode,会先对children进行判断,如果只是简单的一维数组,则遍历创建每一个子vnode;如果children是二维即以上的,则会将children拆开并合并成一维数组,然后遍历创建每一个vnode。在创建好后,在创建父vnode,然后将子vnode作为父vnode的children进行赋值,从而完成vnode的创建过程。