经过compile编译模板字符串变成了render函数,在src/core/instance/render.js中,通过vnode = render.call(vm._renderProxy, vm.$createElement)调用了render方法并最终返回了一个VNode对象实例,即Vue中的虚拟Dom,基本定义如下

export default class VNode {
  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag v // 标签名
    this.data = data // 结点相关属性数据
    this.children = children // 子节点
    this.text = text // 文本
    this.elm = elm // dom元素 
    this.ns = undefined // 命名空间
    this.context = context // VNode上下文对象
    ...... 
    this.key = data && data.key // key
    this.componentOptions = componentOptions // VNode对象如果对应的是一个自定义组件,componentOptions保存组件相关事件、props数据等
    this.componentInstance = undefined // VNode对象如果对应的是一个自定义组件,componentInstance保存相对应的vue实例
    this.parent = undefined // 当前自定义组件在父组件中的vnode
    this.raw = false
    this.isStatic = false // 是否静态节点
    .....
    this.isOnce = false // 是否为v-once元素的VNode对象
  }

  get child (): Component | void {
    return this.componentInstance
  }

}

后面还定义了一些创建简单的VNode的方法,如createEmptyVNodereateEmptyVNodecreateTextVNodecloneVNode

前面说过,调用_init方法的最后,会调用vm._render来生成VNode节点,在src/core/instance/render.js可以找到这个方法, 在这个方法里面,就会调用vnode = render.call(vm._renderProxy, vm.$createElement)来生成VNode,其中在这文件有两个createElement

vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

其中vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)是编译模板生成的render函数执行时调用的, vm.$createElement是自己添加render函数时,作为参数传递给render函数

src/core/vdom/create-element.jscreateElement

export function createElement (
  context: Component, // 当前的上下文对象,哪一个vm
  tag: any, //  标签名
  data: any, // 节点相关的属性,比如key,ref,attrs等等属性
  children: any, // children
  normalizationType: any, // 子元素扁平化处理的级别
  alwaysNormalize: boolean // 总是扁平化处理
): VNode | Array<VNode> {
  // 如果判定为true,说明该元素没有相关属性
  if (Array.isArray(data) || isPrimitive(data)) {
    normalizationType = children
    children = data
    data = undefined
  }
  if (isTrue(alwaysNormalize)) {
    // 最高级别的扁平化处理
    normalizationType = ALWAYS_NORMALIZE
  }
  return _createElement(context, tag, data, children, normalizationType)
}

调用_createElement

export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
): VNode | Array<VNode> {
	......
  if (!tag) {
    // 如果tag为空,创建一个空的VNode
    return createEmptyVNode()
  }
  // 在Vue中,如果设置对象或者数组为key的时候,会报错
  if (process.env.NODE_ENV !== 'production' &&
    isDef(data) && isDef(data.key) && !isPrimitive(data.key)
  ) {
    if (!__WEEX__ || !('@binding' in data.key)) {
      warn(
        'Avoid using non-primitive value as key, ' +
        'use string/number value instead.',
        context
      )
    }
  }
  // 如果只有一个children,则当成slot解析
  if (Array.isArray(children) &&
    typeof children[0] === 'function'
  ) {
    data = data || {}
    data.scopedSlots = { default: children[0] }
    children.length = 0
  }
  // 扁平化处理
  if (normalizationType === ALWAYS_NORMALIZE) {
    children = normalizeChildren(children)
  } else if (normalizationType === SIMPLE_NORMALIZE) {
    children = simpleNormalizeChildren(children)
  }
  let vnode, ns
  if (typeof tag === 'string') {
    let Ctor
    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
    if (config.isReservedTag(tag)) {
      // 如果tag为String,且为平台保留的标签,直接创建VNode
      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对象
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
      )
    }
  } else {
    // 如果tab不是字符串,也是直接createComponent
    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()
  }
}

createComponent可以创建自定义组件

export function createComponent (
  Ctor: Class<Component> | Function | Object | void,
  data: ?VNodeData,
  context: Component,
  children: ?Array<VNode>,
  tag?: string
): VNode | Array<VNode> | void {
  // 如果Ctor为空,跳过此步骤,对应的实际情况就是传入的参数并不是字符串或者自定义组件
  if (isUndef(Ctor)) {
    return
  }
	
	// baseCtor其实就是指的Vue对象
  const baseCtor = context.$options._base

  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor)
  }
	......
  data = data || {}
	
	// 递归合并父对象上的options
  resolveConstructorOptions(Ctor)

	// 处理v-model指令
  if (isDef(data.model)) {
    transformModel(Ctor.options, data)
  }
	
	// 抽取props属性
  const propsData = extractPropsFromVNodeData(data, Ctor, tag)

  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children)
  }
	
	// 处理组件.native的事件,表示组件上的事件
  const listeners = data.on
  data.on = data.nativeOn
		
	// 处理抽象组件中的slot,如keep-alive
  if (isTrue(Ctor.options.abstract)) {
    const slot = data.slot
    data = {}
    if (slot) {
      data.slot = slot
    }
  }
	
	// 合并一些钩子函数
  installComponentHooks(data)

  const name = Ctor.options.name || tag
  
  // 生成VNode
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )
	
  ......

  return vnode
}

extractPropsFromVNodeData

function extractProps (data: VNodeData, Ctor: Class<Component>, tag?: string): ?Object {
  const propOptions = Ctor.options.props
  // 组件没有props,return
  if (!propOptions) {
    return
  }
  const res = {}
  // domProps表示input的value、option的selected等属性
  // attrs表父组件绑定在子元素上的属性值
  const { attrs, props, domProps } = data
  if (attrs || props || domProps) {
    for (const key in propOptions) {
      // 将驼峰命名转化为连线命名
			// aB -> a-b
      const altKey = hyphenate(key)
      .....
      
      // 遍历找到props、attrs、domProps中的属性
      // 如果没有传递preserve参数,则表示找到该key的值时删除对应的属性
      // 最后返回res,并且只留下props上的属性
      checkProp(res, props, key, altKey, true) ||
      checkProp(res, attrs, key, altKey) ||
      checkProp(res, domProps, key, altKey)
    }
  }
  return res
}

最后,对于自定义组件,会创建一个vnode对象,并返回一个标签名为vue-component-cid-name格式的VNode对象。

上面说的扁平化处理其实就是将多维的数组,合并转换成一个一维的数组

simpleNormalizeChildren简单的扁平化处理

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
}

normalizeChildren

export function normalizeChildren (children: any): ?Array<VNode> {
  return isPrimitive(children)
  	// 如果传入的children是字符串或者数字,则直接返回文本结点数组
    ? [createTextVNode(children)]
    : Array.isArray(children)
			// 如果传入的children是数组,调用normalizeArrayChildren
      ? normalizeArrayChildren(children)
      : undefined
}

normalizeArrayChildren

function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
  const res = []
  let i, c, lastIndex, last
  for (i = 0; i < children.length; i++) {
    c = children[i]
    // 如果该元素为undefined或null或Boolean类型的值,continue
    if (isUndef(c) || typeof c === 'boolean') continue
    lastIndex = res.length - 1
    last = res[lastIndex]
    if (Array.isArray(c)) {
      if (c.length > 0) {
        // c是一个数组,则递归的执行normalizeArrayChildren方法
        c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
        if (isTextNode(c[0]) && isTextNode(last)) {
          res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
          c.shift()
        }
        res.push.apply(res, c)
      }
    } else if (isPrimitive(c)) {
      // 如果最后一个节点是文本节点
      if (isTextNode(last)) {
        // 合并last.text和c
        res[lastIndex] = createTextVNode(last.text + c)
      } else if (c !== '') {
        // 如果最后一个节点不是文本节点,且不为空
        // 创建一个空文本节点
        res.push(createTextVNode(c))
      }
    } else {
      // 如果c是一个文本VNode对象,且res中最后一个元素也是文本结点
      // 合并两个VNode为一个
      if (isTextNode(c) && isTextNode(last)) {
        res[lastIndex] = createTextVNode(last.text + c.text)
      } else {
        // 设置VNode的key
        if (isTrue(children._isVList) &&
          isDef(c.tag) &&
          isUndef(c.key) &&
          isDef(nestedIndex)) {
          c.key = `__vlist${nestedIndex}_${i}__`
        }
        res.push(c)
      }
    }
  }
  return res
}