方法开始会做判断,如果data是响应式的数据,component的is属性不是真值的时候,都会去调用createEmptyVNode方法,创建一个空的vnode。 接下来,根据normalizationType的值,调用normalizeChildren或simpleNormalizeChildren方法对参数children进行处理。这两个方法定义在 src/core/vdom/helpers/normalize-children.js 文件下。
// 1. When the children contains components - because a functional component // may return an Array instead of a single root. In this case, just a simple // normalization is needed - if any child is an Array, we flatten the whole // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep // because functional components already normalize their own children. export function simpleNormalizeChildren (children: any) { for (let i = 0; i < children.length; i++) { if (Array.isArray(children[i])) { return Array.prototype.concat.apply([], children) } } return children } // 2. When the children contains constructs that always generated nested Arrays, // e.g. <template>, <slot>, v-for, or when the children is provided by user // with hand-written render functions / JSX. In such cases a full normalization // is needed to cater to all possible types of children values. export function normalizeChildren (children: any): ?Array<VNode> { return isPrimitive(children) ? [createTextVNode(children)] : Array.isArray(children) ? normalizeArrayChildren(children) : undefined }
normalizeChildren和simpleNormalizeChildren的目的都是将children数组扁平化处理,最终返回一个vnode的一维数组。
simpleNormalizeChildren是针对函数式组件做处理,所以只需要考虑children是二维数组的情况。 normalizeChildren方法会考虑children是多层嵌套的数组的情况。normalizeChildren开始会判断children的类型,如果children是基础类型,直接创建文本vnode,如果是数组,调用normalizeArrayChildren方法,并在normalizeArrayChildren方法里面进行递归调用,最终将children转成一维数组。
接下来,继续看_createElement方法,如果tag参数的类型不是String类型,是组件的话,调用createComponent创建vnode。如果tag是String类型,再去判断tag是否是html的保留标签,是否是不认识的节点,通过调用new VNode(),传入不同的参数来创建vnode实例。
无论是哪种情况,最终都是通过VNode这个class来创建vnode,下面是类VNode的源码,在文件 src/core/vdom/vnode.js 中定义
export default class VNode { tag: string | void; data: VNodeData | void; children: ?Array<VNode>; text: string | void; elm: Node | void; ns: string | void; context: Component | void; // rendered in this component's scope key: string | number | void; componentOptions: VNodeComponentOptions | void; componentInstance: Component | void; // component instance parent: VNode | void; // component placeholder node // strictly internal raw: boolean; // contains raw HTML? (server only) isStatic: boolean; // hoisted static node isRootInsert: boolean; // necessary for enter transition check isComment: boolean; // empty comment placeholder? isCloned: boolean; // is a cloned node? isOnce: boolean; // is a v-once node? asyncFactory: Function | void; // async component factory function asyncMeta: Object | void; isAsyncPlaceholder: boolean; ssrContext: Object | void; fnContext: Component | void; // real context vm for functional nodes fnOptions: ?ComponentOptions; // for SSR caching devtoolsMeta: ?Object; // used to store functional render context for devtools fnScopeId: ?string; // functional scope id support constructor ( tag?: string, data?: VNodeData, children?: ?Array<VNode>, text?: string, elm?: Node, context?: Component, componentOptions?: VNodeComponentOptions, asyncFactory?: Function ) { this.tag = tag // 标签名 this.data = data // 当前节点数据 this.children = children // 子节点 this.text = text // 文本 this.elm = elm // 对应的真实DOM节点 this.ns = undefined // 命名空间 this.context = context // 当前节点上下文 this.fnContext = undefined // 函数化组件上下文 this.fnOptions = undefined // 函数化组件配置参数 this.fnScopeId = undefined // 函数化组件ScopeId this.key = data && data.key // 子节点key属性 this.componentOptions = componentOptions // 组件配置项 this.componentInstance = undefined // 组件实例 this.parent = undefined // 父节点 this.raw = false // 是否是原生的HTML片段或只是普通文本 this.isStatic = false // 静态节点标记 this.isRootInsert = true // 是否作为根节点插入 this.isComment = false // 是否为注释节点 this.isCloned = false // 是否为克隆节点 this.isOnce = false // 是否有v-once指令 this.asyncFactory = asyncFactory // 异步工厂方法 this.asyncMeta = undefined // 异步Meta this.isAsyncPlaceholder = false // 是否异步占位 } // DEPRECATED: alias for componentInstance for backwards compat. /* istanbul ignore next */ get child (): Component | void { return this.componentInstance } }
VNode类定义的数据,都是用来描述VNode的。
至此,render函数创建vdom的源码就分析完了,我们简单的总结梳理一下。