# createElm

function createElm(
  vnode,
  insertedVnodeQueue,
  parentElm,
  refElm, // nextSibiling
  nested,
  ownerArray,
  index
) {
  if (isDef(vnode.elm) && isDef(ownerArray)) {
    vnode = ownerArray[index] = cloneVNode(vnode)
  }
  vnode.isRootInsert = !nested
  /*
   * createComponent 和 render 中的不是同一个函数
   * 如果 vnode 是一个组件, 则执行 init 钩子,创建组件实例并挂载
   * 然后为组件执行各个模块的 create 钩子
   * 如果组件被 keep-alive 包裹, 则激活组件
   * 组件走动这就 return 了
  */
    
  if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) return
  
  // 不是组件 Root
  const data = vnode.data
  const children = vnode.children
  const tag = vnode.tag
  if (isDef(tag)) {
    if (process.env.NODE_ENV !== 'production') {
      if (data && data.pre) {
        creatingElmInPre++
      }
      // 未知标签
      if (isUnKnownElement(vnode, creatingElmInPre)) {
        warn('...')
      }
    }
    
    // 创建新节点
    vnode.elm = vnode.ns
      ? nodeOps.createElementNS(vnode.ns, tag)
      : nodeOps.createElement(tag, vnode)
    // 如果 style 里设置了 scope 则为标签添加 hash
    setScope(vnode)
    
    // 递归创建所有子节点
    createChildren(vnode, children, insertedVnodeQueue)
    if (isDef(data)) {
      invokeCreateHooks(vnode, insertedVnodeQueue)
    }
    // 将节点插入父节点, 如果有 refElm 则将 DOM 插入到 refElm 之前
    insert(parentElm, vnode.elm, refElm)
    if (process.env.NODE_ENV !== 'production' && data && data.pre) {
      creatingElmIInVPre--
    }
  } else if (isTrue(vnode.isComment)) {
    // 注释节点
    vnode.elm = nodeOps.createComment(vnode.text)
    insert(parentElm, vnode.elm, refElm)
  } else {
    // 文本节点
    vnode.elm = nodeOps.createTextNode(vnode.text)
    insert(parentElm, vnode.elm, refElm)
  }
}

# createComponent

function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
  // 获取 vnode.data 对象
  let i = vnode.data
  if (isDef(i)) {
    // 未创建实例的组件都会先返回false,创建对应的 element 并 insert
    // 组件实例被 keepAlive 包裹,不会销毁 componentInstance 则会直接 insert 不会重新 createElm
    const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
    
    // 执行 vnode.data.init 函数 
    // _c 会注册 init hooks 会将 VNode.componentInstance 设置为对应的 Vue 实例
    if (isDef(i = i.hook) && isDef(i = i.init)) {
      i(vnode, false/* hydrating */)
    }
    
    if (isDef(vnode.componentInstance)) {
      initComponent(vnode, insertdVnodeQueue)
      insert(parentElm, vnode.elm, refElm)
      // 如果存在 componentInstance 并且 keepAlive (缓存) 则不
      if (isTrue(isReactivated)) {
        // 组件被 keep-alive 包裹的情况,激活组件
        reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
      }
      return true
    }
  }
}

# createChildren

function createChildren(vnode, children, insertedVnodeQueue) {
  if (Array.isArray(children)) {
    for (let i = 0; i < children.length; i++) {
      createElm(children[i], insertedQueue, vnode.elm, null, true, children, i)
    }
  } else if (isPrimitive(vnode.text)) {
    nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)))
  }
}