text: /* @flow */
  
  import VNode from './vnode'
  import { resolveConstructorOptions } from 'core/instance/init'
  import { queueActivatedComponent } from 'core/observer/scheduler'
  import { createFunctionalComponent } from './create-functional-component'
  
  import {
    warn,
    isDef,
    isUndef,
    isTrue,
    isObject
  } from '../util/index'
  
  import {
    resolveAsyncComponent,
    createAsyncPlaceholder,
    extractPropsFromVNodeData
  } from './helpers/index'
  
  import {
    callHook,
    activeInstance,
    updateChildComponent,
    activateChildComponent,
    deactivateChildComponent
  } from '../instance/lifecycle'
  
  import {
    isRecyclableComponent,
    renderRecyclableComponentTemplate
  } from 'weex/runtime/recycle-list/render-component-template'
  
  // inline hooks to be invoked on component VNodes during patch
  const componentVNodeHooks = {
    init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
      if (
        vnode.componentInstance &&
        !vnode.componentInstance._isDestroyed &&
        vnode.data.keepAlive
      ) {
        // kept-alive components, treat as a patch
        const mountedNode: any = vnode // work around flow
        componentVNodeHooks.prepatch(mountedNode, mountedNode)
      } else {
        const child = vnode.componentInstance = createComponentInstanceForVnode(
          vnode,
          activeInstance
        )
        child.$mount(hydrating ? vnode.elm : undefined, hydrating)
      }
    },
  
    prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
      // 组件更新：拿到新的 vnode 的组件配置以及组件实例，去执行 updateChildComponent
      const options = vnode.componentOptions
      const child = vnode.componentInstance = oldVnode.componentInstance
      updateChildComponent(
        child,
        options.propsData, // updated props
        options.listeners, // updated listeners
        vnode, // new parent vnode
        options.children // new children
      )
    },
  
    insert (vnode: MountedComponentVNode) {
      const { context, componentInstance } = vnode
      if (!componentInstance._isMounted) {
        componentInstance._isMounted = true
        callHook(componentInstance, 'mounted')
      }
      if (vnode.data.keepAlive) {
        if (context._isMounted) {
          // vue-router#1212
          // During updates, a kept-alive component's child components may
          // change, so directly walking the tree here may call activated hooks
          // on incorrect children. Instead we push them into a queue which will
          // be processed after the whole patch process ended.
          queueActivatedComponent(componentInstance)
        } else {
          activateChildComponent(componentInstance, true /* direct */)
        }
      }
    },
  
    destroy (vnode: MountedComponentVNode) {
      const { componentInstance } = vnode
      if (!componentInstance._isDestroyed) {
        if (!vnode.data.keepAlive) {
          componentInstance.$destroy()
        } else {
          deactivateChildComponent(componentInstance, true /* direct */)
        }
      }
    }
  }
  
  const hooksToMerge = Object.keys(componentVNodeHooks)
  
  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
    }
  
    // 根实例
    // 在 initGlobalAPI 中定义的 Vue.options._base = Vue
    const baseCtor = context.$options._base
  
    // createComponent
    // 因为我们这样引用组件，所以为一个Object
    // export default {
    //   name: 'app',
    //   components: {
    //     HelloWorld
    //   }
    // }
    if (isObject(Ctor)) {
      // 一：
      // 通过 Vue 的 extend 方法，生成子类构造函数，使得子类也有Vue根实例的一些方法
      // 其实就是构造Vue的子类
      // src/core/global-api/extend.js
      Ctor = baseCtor.extend(Ctor)
    }
  
    // if at this stage it's not a constructor or an async component factory,
    // reject.
    if (typeof Ctor !== 'function') {
      if (process.env.NODE_ENV !== 'production') {
        warn(`Invalid Component definition: ${String(Ctor)}`, context)
      }
      return
    }
  
    // 异步组件
    let asyncFactory
    if (isUndef(Ctor.cid)) {
      asyncFactory = Ctor
      Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
      if (Ctor === undefined) {
        // return a placeholder node for async component, which is rendered
        // as a comment node but preserves all the raw information for the node.
        // the information will be used for async server-rendering and hydration.
        return createAsyncPlaceholder(
          asyncFactory,
          data,
          context,
          children,
          tag
        )
      }
    }
  
    data = data || {}
  
    // 解析构造函数选项，如果全局混合后应用
    // 创建组件构造函数
    resolveConstructorOptions(Ctor)
  
    // transform component v-model data into props & events
    if (isDef(data.model)) {
      transformModel(Ctor.options, data)
    }
  
    // extract props
    const propsData = extractPropsFromVNodeData(data, Ctor, tag)
  
    // 函数式组件
    if (isTrue(Ctor.options.functional)) {
      return createFunctionalComponent(Ctor, propsData, data, context, children)
    }
  
    // extract listeners, since these needs to be treated as
    // child component listeners instead of DOM listeners
    const listeners = data.on
    // replace with listeners with .native modifier
    // so it gets processed during parent component patch.
    data.on = data.nativeOn
  
    if (isTrue(Ctor.options.abstract)) {
      // abstract components do not keep anything
      // other than props & listeners & slot
  
      // work around flow
      const slot = data.slot
      data = {}
      if (slot) {
        data.slot = slot
      }
    }
  
    // 二：安装组件钩子函数
    installComponentHooks(data)
  
    // return a placeholder vnode
    // 三：实例化VNode并返回。需要注意组件vnode没有children，这点在之后的patch在分析。
    const name = Ctor.options.name || tag
    const vnode = new VNode(
      `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
      data, undefined, undefined, undefined, context,
      { Ctor, propsData, listeners, tag, children },
      asyncFactory
    )
    // Weex specific: invoke recycle-list optimized @render function for
    // extracting cell-slot template.
    // https://github.com/Hanks10100/weex-native-directive/tree/master/component
    /* istanbul ignore if */
    if (__WEEX__ && isRecyclableComponent(vnode)) {
      return renderRecyclableComponentTemplate(vnode)
    }
  
    return vnode
  }
  
  export function createComponentInstanceForVnode (
    vnode: any, // we know it's MountedComponentVNode but flow doesn't
    parent: any, // activeInstance in lifecycle state
  ): Component {
    const options: InternalComponentOptions = {
      _isComponent: true,
      _parentVnode: vnode,
      parent
    }
    // check inline-template render functions
    const inlineTemplate = vnode.data.inlineTemplate
    if (isDef(inlineTemplate)) {
      options.render = inlineTemplate.render
      options.staticRenderFns = inlineTemplate.staticRenderFns
    }
    return new vnode.componentOptions.Ctor(options)
  }
  
  // 把componentVNodeHooks的钩子函数合并到data.hook里，在VNode执行patch过程中执行相关钩子函数。
  // 注意：合并过程中如果已经存在，那么执行mergeHook返回一个merged函数，最终执行的时候，依次执行这两个钩子函数
  function installComponentHooks (data: VNodeData) {
    const hooks = data.hook || (data.hook = {})
    for (let i = 0; i < hooksToMerge.length; i++) {
      const key = hooksToMerge[i]
      const existing = hooks[key]
      const toMerge = componentVNodeHooks[key]
      if (existing !== toMerge && !(existing && existing._merged)) {
        hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge
      }
    }
  }
  
  function mergeHook (f1: any, f2: any): Function {
    const merged = (a, b) => {
      // flow complains about extra args which is why we use any
      f1(a, b)
      f2(a, b)
    }
    merged._merged = true
    return merged
  }
  
  // transform component v-model info (value and callback) into
  // prop and event handler respectively.
  function transformModel (options, data: any) {
    const prop = (options.model && options.model.prop) || 'value'
    const event = (options.model && options.model.event) || 'input'
    ;(data.props || (data.props = {}))[prop] = data.model.value
    const on = data.on || (data.on = {})
    if (isDef(on[event])) {
      on[event] = [data.model.callback].concat(on[event])
    } else {
      on[event] = data.model.callback
    }
  }
  