text: /* @flow */
  
  import {
    warn,
    nextTick,
    emptyObject,
    handleError,
    defineReactive
  } from '../util/index'
  
  import { createElement } from '../vdom/create-element'
  import { installRenderHelpers } from './render-helpers/index'
  import { resolveSlots } from './render-helpers/resolve-slots'
  import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots'
  import VNode, { createEmptyVNode } from '../vdom/vnode'
  
  import { isUpdatingChildComponent } from './lifecycle'
  
  export function initRender (vm: Component) {
    vm._vnode = null // the root of the child tree
    vm._staticTrees = null // v-once cached trees
    const options = vm.$options
    const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
    const renderContext = parentVnode && parentVnode.context
    vm.$slots = resolveSlots(options._renderChildren, renderContext)
    vm.$scopedSlots = emptyObject
    // bind the createElement fn to this instance
    // so that we get proper render context inside it.
    // args order: tag, data, children, normalizationType, alwaysNormalize
    // internal version is used by render functions compiled from templates
    vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
    // normalization is always applied for the public version, used in
    // user-written render functions.
    vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
  
    // $attrs & $listeners are exposed for easier HOC creation.
    // they need to be reactive so that HOCs using them are always updated
    const parentData = parentVnode && parentVnode.data
  
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
        !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
      }, true)
      defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
        !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
      }, true)
    } else {
      defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
      defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
    }
  }
  
  export let currentRenderingInstance: Component | null = null
  
  // for testing only
  export function setCurrentRenderingInstance (vm: Component) {
    currentRenderingInstance = vm
  }
  
  export function renderMixin (Vue: Class<Component>) {
    // install runtime convenience helpers
    installRenderHelpers(Vue.prototype)
  
    Vue.prototype.$nextTick = function (fn: Function) {
      return nextTick(fn, this)
    }
  
    Vue.prototype._render = function (): VNode {
      const vm: Component = this
      // 从 options 中拿到 render 函数
      const { render, _parentVnode } = vm.$options
  
      if (_parentVnode) {
        vm.$scopedSlots = normalizeScopedSlots(
          _parentVnode.data.scopedSlots,
          vm.$slots,
          vm.$scopedSlots
        )
      }
  
      // set parent vnode. this allows render functions to have access
      // to the data on the placeholder node.
      vm.$vnode = _parentVnode
      // render self
      let vnode
      try {
        // There's no need to maintain a stack because all render fns are called
        // separately from one another. Nested component's render fns are called
        // when parent component is patched.
        currentRenderingInstance = vm // 记录一下当前在渲染中的实例
        // 这里 vm._renderProxy 就是 vm 自己，开发模式可能是一个 proxy 对象
        vnode = render.call(vm._renderProxy, vm.$createElement)
      } catch (e) {
        handleError(e, vm, `render`)
        // 获取错误提醒的 vnode，或之前的 vnode，避免空白节点的情况
        if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
          try {
            vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
          } catch (e) {
            handleError(e, vm, `renderError`)
            vnode = vm._vnode
          }
        } else {
          vnode = vm._vnode
        }
      } finally {
        currentRenderingInstance = null
      }
      // if the returned array contains only a single node, allow it
      if (Array.isArray(vnode) && vnode.length === 1) {
        vnode = vnode[0]
      }
      // 如果 vnode 不是 VNode 实例，就获取空 vnode
      if (!(vnode instanceof VNode)) {
        // 如果 vnode 是一个数组，说明 template 根元素有多个元素就提醒
        if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
          warn(
            'Multiple root nodes returned from render function. Render function ' +
            'should return a single root node.',
            vm
          )
        }
        vnode = createEmptyVNode()
      }
      // set parent
      vnode.parent = _parentVnode
      return vnode
    }
  }
  