返回

揭开Vue源码的奥秘:Vue实例挂载的实现

前端

Vue 实例挂载的实现

在上一节中,我们看到会调用 vm.$mount(vm.$options.el); 来挂载实例,那么 $mount 是什么?$mount 方法在多个文件中都有定义,分别对应着不同的挂载场景。

src/core/instance/index.js

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

这是 Vue.prototype.$mount 方法的定义,它接受两个参数:elhydrating

  • el 是需要挂载的元素。它可以是字符串选择器、DOM 元素或组件。
  • hydrating 是一个布尔值,表示是否正在进行服务端渲染。

$mount 方法首先会检查 el 是否存在。如果 el 不存在,则直接返回。否则,它会调用 mountComponent 方法来挂载组件。

src/core/instance/lifecycle.js

export function mountComponent (
  vm: Component,
  el: Element | null,
  hydrating?: boolean
): Component {
  vm.$el = el
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode
  }
  callHook(vm, 'beforeMount')

  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      mark('vue-perf-start:mount')
      const vnode = vm._render()
      mark('vue-perf-end:mount')
      measure(`vue-perf:mount: ${name}`, 'vue-perf-start:mount', 'vue-perf-end:mount')
      callHook(vm, 'mounted')
    }
  } else {
    updateComponent = () => {
      callHook(vm, 'mounted')
    }
  }

  vm._update(vm._render(), hydrating)

  // queue the update, so that the mounted hook is called
  // after the first update.
  nextTick(updateComponent)

  return vm
}

mountComponent 方法首先会设置 vm.$elel。然后,它会检查 vm.$options.render 是否存在。如果不存在,则会创建一个空的虚拟节点。

接下来,它会调用 callHook(vm, 'beforeMount') 来触发 beforeMount 生命周期钩子。

然后,它会创建一个 updateComponent 函数。这个函数将在组件更新时被调用。

接下来,它会调用 vm._update(vm._render(), hydrating) 来更新组件。

最后,它会调用 nextTick(updateComponent) 来将 updateComponent 函数加入到下一个事件循环中。这确保了 mounted 生命周期钩子在第一次更新之后被调用。

总结

Vue 实例挂载的实现是一个复杂的过程,涉及到多个文件和函数。它需要先找到挂载的元素,然后创建虚拟节点,再调用一系列生命周期钩子,最后更新组件。