细读Vue2.6.14 Core 源码(7): $mount

369 阅读1分钟

细读Vue2.6.14 Core 源码(7): $mount

    /**
        @date 2021-10-10
        @description 细读Vue2.6.14 Core 源码(7): $mount
    */

壹(序)

在Vue项目中的main.js中,一般会有 new Vue 的操作,这最后会调用 mount函数,本章从mount 函数,本章从 mount 出发,完成一个Vue实例从声明到挂载的过程。

贰($mount)

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  // 查找element
  el = el && inBrowser ? query(el) : undefined
  // 挂载
  return mountComponent(this, el, hydrating)
}

叁(mountComponent)

/**
 * mountComponent 函数在调用 $mount 函数时调用
 * 一般的Vue项目最开始的 $mount 函数是在 main.js 中 new Vue 时调用
 * 其他组件的 $mount 会在componentVNodeHooks的init中调用
 */
export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  // 没有render函数则生成一个空的VNode
  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
        )
      }
    }
  }
  // 调用 beforeMount 钩子函数
  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)
      vm._update(vnode, hydrating)
      mark(endTag)
      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
  // 为 updateComponent 配置一个 watcher,在调用时会触发 beforeUpdate 钩子函数
  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
  /**
   * 这里是调用第一个Vm的mounted方法,也就是uid为0的vm
   * 比如一个由Vue官方脚手架生成的Vue项目的main.js中,有如下声明
   * new Vue({
   *  render: (h) => h(App),
   * }).$mount('#app');
   * 就是调用这里的 mounted 钩子函数
   * 而组件的 mounted 钩子函数会在componentVNodeHooks中的insert中调用
   */
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}

肆($mount)

到这里就已经完成了从 new Vue 到调用mounted函数的所有过程,此时已经完全展示出页面。

终(导航)

细读Vue2.6.14 Core 源码(1): 入口

细读Vue2.6.14 Core 源码(2): After Vue

细读Vue2.6.14 Core 源码(3): initGlobalAPI

细读Vue2.6.14 Core 源码(4): _init

细读Vue2.6.14 Core 源码(5): initState

细读Vue2.6.14 Core 源码(6): defineReactive

细读Vue2.6.14 Core 源码(7): $mount

细读Vue2.6.14 Core 源码(8): $createElement