text: /* @flow */
  
  import config from '../config'
  import Watcher from '../observer/watcher'
  import { mark, measure } from '../util/perf'
  import { createEmptyVNode } from '../vdom/vnode'
  import { updateComponentListeners } from './events'
  import { resolveSlots } from './render-helpers/resolve-slots'
  import { toggleObserving } from '../observer/index'
  import { pushTarget, popTarget } from '../observer/dep'
  
  import {
    warn,
    noop,
    remove,
    emptyObject,
    validateProp,
    invokeWithErrorHandling
  } from '../util/index'
  
  export let activeInstance: any = null
  export let isUpdatingChildComponent: boolean = false
  
  export function setActiveInstance(vm: Component) {
    const prevActiveInstance = activeInstance
    activeInstance = vm
    return () => {
      activeInstance = prevActiveInstance
    }
  }
  
  
  export function initLifecycle (vm: Component) {
    const options = vm.$options
  
    // locate first non-abstract parent
    let parent = options.parent
    if (parent && !options.abstract) {
      while (parent.$options.abstract && parent.$parent) {
        parent = parent.$parent
      }
      parent.$children.push(vm)
    }
  
    // 初始化值，这里说明，子组件创建的时候，父组件已经存在了
    vm.$parent = parent
    vm.$root = parent ? parent.$root : vm
  
    vm.$children = []
    vm.$refs = {}
  
    // _开头的都是内部用的，不希望用户使用
    vm._watcher = null
    vm._inactive = null
    vm._directInactive = false
    vm._isMounted = false
    vm._isDestroyed = false
    vm._isBeingDestroyed = false
  }
  
  export function lifecycleMixin (Vue: Class<Component>) {
    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
      const vm: Component = this
      const prevEl = vm.$el  // 之前的dom
      const prevVnode = vm._vnode  // 之前的虚拟节点
      const restoreActiveInstance = setActiveInstance(vm)
      vm._vnode = vnode
      // Vue.prototype.__patch__ is injected in entry points
      // based on the rendering backend used.
      if (!prevVnode) {  // 木有老的就初始化
        // initial render
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
      } else {
        // updates
        // prevVnode，这里看上去就是path新老差距然后补丁算法
        vm.$el = vm.__patch__(prevVnode, vnode)
      }
      restoreActiveInstance()
      // update __vue__ reference
      if (prevEl) {
        prevEl.__vue__ = null
      }
      if (vm.$el) {
        vm.$el.__vue__ = vm
      }
      // if parent is an HOC, update its $el as well
      if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
        vm.$parent.$el = vm.$el
      }
      // updated hook is called by the scheduler to ensure that children are
      // updated in a parent's updated hook.
    }
     
    // 一切不按照预期的页面显示，forceUpdate一下
    Vue.prototype.$forceUpdate = function () {
      const vm: Component = this
      if (vm._watcher) {
        vm._watcher.update()
      }
    }
  
    Vue.prototype.$destroy = function () {
      const vm: Component = this
      if (vm._isBeingDestroyed) {
        return
      }
      callHook(vm, 'beforeDestroy')
      vm._isBeingDestroyed = true
      // remove self from parent
      const parent = vm.$parent
      if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
        remove(parent.$children, vm)
      }
      // teardown watchers
      if (vm._watcher) {
        vm._watcher.teardown()
      }
      let i = vm._watchers.length
      while (i--) {
        vm._watchers[i].teardown()
      }
      // remove reference from data ob
      // frozen object may not have observer.
      if (vm._data.__ob__) {
        vm._data.__ob__.vmCount--
      }
      // call the last hook...
      vm._isDestroyed = true
      // invoke destroy hooks on current rendered tree
      vm.__patch__(vm._vnode, null)
      // fire destroyed hook
      callHook(vm, 'destroyed')
      // turn off all instance listeners.
      vm.$off()
      // remove __vue__ reference
      if (vm.$el) {
        vm.$el.__vue__ = null
      }
      // release circular reference (#6759)
      if (vm.$vnode) {
        vm.$vnode.parent = null
      }
    }
  }
  
  // $.mout('#app')
  
  // new Vue({}).$mount('#app')
  // 这里就是初始化vue实例的时候的$mout
  
  export function mountComponent (
    vm: Component, 
    el: ?Element,
    hydrating?: boolean
  ): Component {
    vm.$el = el
    if (!vm.$options.render) {
      vm.$options.render = createEmptyVNode
      // if (process.env.NODE_ENV !== 'production') {
      //   /* istanbul ignore if */
      //   if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
      //     vm.$options.el || el) {
      //     warn(
      //       'You are using the runtime-only build of Vue where the template ' +
      //       'compiler is not available. Either pre-compile the templates into ' +
      //       'render functions, or use the compiler-included build.',
      //       vm
      //     )
      //   } else {
      //     warn(
      //       'Failed to mount component: template or render function not defined.',
      //       vm
      //     )
      //   }
      // }
    }
  
  
    callHook(vm, 'beforeMount')
  
  
    let updateComponent
  
  
    /* istanbul ignore if */
    // 如果不是生产环境，并且打开了性能调试之类的东东
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      updateComponent = () => {
        const name = vm._name
        const id = vm._uid
        const startTag = `vue-perf-start:${id}`
        const endTag = `vue-perf-end:${id}`
  
        mark(startTag)
        const vnode = vm._render()
        mark(endTag)
        measure(`vue ${name} render`, startTag, endTag)
  
        mark(startTag)
  
        // vnode = vm._render(),render函数会返回虚拟dom，这里好像是对
        // 对虚拟dom进行更新，八成是对dom的真实操作是在这
        vm._update(vnode, hydrating)
  
  
  
        mark(endTag)
        // 这里看上去是mark开始标签和结束标签，有点不值所云
        measure(`vue ${name} patch`, startTag, endTag)
      }
    } else {
      updateComponent = () => {
        vm._update(vm._render(), hydrating)
      }
    }
  
    // we set this to vm._watcher inside the watcher's constructor
    // since the watcher's initial patch may call $forceUpdate (e.g. inside child
    // component's mounted hook), which relies on vm._watcher being already defined
  
    // 这里可以看出，是一个Vue组件一个Watcher。除非用户写了￥watcher
    // 一个Dep对应多个Watcher
  
    new Watcher(vm, updateComponent, noop, {
      before () {
        if (vm._isMounted && !vm._isDestroyed) {
          callHook(vm, 'beforeUpdate')
        }
      }
    }, true /* isRenderWatcher */)
    hydrating = false
  
    // manually mounted instance, call mounted on self
    // mounted is called for render-created child components in its inserted hook
    if (vm.$vnode == null) {
      vm._isMounted = true
      callHook(vm, 'mounted')
    }
    return vm
  }
  
  export function updateChildComponent (
    vm: Component,
    propsData: ?Object,
    listeners: ?Object,
    parentVnode: MountedComponentVNode,
    renderChildren: ?Array<VNode>
  ) {
    if (process.env.NODE_ENV !== 'production') {
      isUpdatingChildComponent = true
    }
  
    // determine whether component has slot children
    // we need to do this before overwriting $options._renderChildren.
  
    // check if there are dynamic scopedSlots (hand-written or compiled but with
    // dynamic slot names). Static scoped slots compiled from template has the
    // "$stable" marker.
    const newScopedSlots = parentVnode.data.scopedSlots
    const oldScopedSlots = vm.$scopedSlots
    const hasDynamicScopedSlot = !!(
      (newScopedSlots && !newScopedSlots.$stable) ||
      (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||
      (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key)
    )
  
    // Any static slot children from the parent may have changed during parent's
    // update. Dynamic scoped slots may also have changed. In such cases, a forced
    // update is necessary to ensure correctness.
    const needsForceUpdate = !!(
      renderChildren ||               // has new static slots
      vm.$options._renderChildren ||  // has old static slots
      hasDynamicScopedSlot
    )
  
    vm.$options._parentVnode = parentVnode
    vm.$vnode = parentVnode // update vm's placeholder node without re-render
  
    if (vm._vnode) { // update child tree's parent
      vm._vnode.parent = parentVnode
    }
    vm.$options._renderChildren = renderChildren
  
    // update $attrs and $listeners hash
    // these are also reactive so they may trigger child update if the child
    // used them during render
    vm.$attrs = parentVnode.data.attrs || emptyObject
    vm.$listeners = listeners || emptyObject
  
    // update props
    if (propsData && vm.$options.props) {
      toggleObserving(false)
      const props = vm._props
      const propKeys = vm.$options._propKeys || []
      for (let i = 0; i < propKeys.length; i++) {
        const key = propKeys[i]
        const propOptions: any = vm.$options.props // wtf flow?
        props[key] = validateProp(key, propOptions, propsData, vm)
      }
      toggleObserving(true)
      // keep a copy of raw propsData
      vm.$options.propsData = propsData
    }
  
    // update listeners
    listeners = listeners || emptyObject
    const oldListeners = vm.$options._parentListeners
    vm.$options._parentListeners = listeners
    updateComponentListeners(vm, listeners, oldListeners)
  
    // resolve slots + force update if has children
    if (needsForceUpdate) {
      vm.$slots = resolveSlots(renderChildren, parentVnode.context)
      vm.$forceUpdate()
    }
  
    if (process.env.NODE_ENV !== 'production') {
      isUpdatingChildComponent = false
    }
  }
  
  function isInInactiveTree (vm) {
    while (vm && (vm = vm.$parent)) {
      if (vm._inactive) return true
    }
    return false
  }
  
  export function activateChildComponent (vm: Component, direct?: boolean) {
    if (direct) {
      vm._directInactive = false
      if (isInInactiveTree(vm)) {
        return
      }
    } else if (vm._directInactive) {
      return
    }
    if (vm._inactive || vm._inactive === null) {
      vm._inactive = false
      for (let i = 0; i < vm.$children.length; i++) {
        activateChildComponent(vm.$children[i])
      }
      callHook(vm, 'activated')
    }
  }
  
  export function deactivateChildComponent (vm: Component, direct?: boolean) {
    if (direct) {
      vm._directInactive = true
      if (isInInactiveTree(vm)) {
        return
      }
    }
    if (!vm._inactive) {
      vm._inactive = true
      for (let i = 0; i < vm.$children.length; i++) {
        deactivateChildComponent(vm.$children[i])
      }
      callHook(vm, 'deactivated')
    }
  }
  
  export function callHook (vm: Component, hook: string) {
    // #7573 disable dep collection when invoking lifecycle hooks
    pushTarget()
    const handlers = vm.$options[hook]
    const info = `${hook} hook`
    if (handlers) {
      for (let i = 0, j = handlers.length; i < j; i++) {
        invokeWithErrorHandling(handlers[i], vm, null, vm, info)
      }
    }
    if (vm._hasHookEvent) {
      vm.$emit('hook:' + hook)
    }
    popTarget()
  }
  