vue框架需要掌握的原理--初始化原理mounted

375 阅读1分钟

大家好,我是鼠目。「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

上一章,大概讲述了初始化过程中的执行,以及初始化时,有哪些全局api被初始化了。new Vue时,会执行Vue.prototype._init()方法,初始化了生命周期和数据后,会执行mounted操作。

那就让我们来看看$mount究竟做了哪些操作

它来自于src/platforms/web/runtime/index.js

// 1.安装补丁函数(更新函数):vdom =》dom,这个函数的主要作用是将虚拟dom转换为真实dom。
Vue.prototype.__patch__ = inBrowser ? patch : noop

// 2.实现$mount方法:调用mountComponent
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

其实,它是返回的是来自于 src/core/instance/lifecycle 的mountComponent函数

// 真正执行了挂载相关的函数
export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  if (!vm.$options.render) {
     //省略 非主要信息
  }
  // 执行了beforeMount相关的方法,哈哈,又出现了生命周期beforeMount,很明显,现在的元素尚未挂载。
  callHook(vm, 'beforeMount')

  // 组件更新函数声明
  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    // 省略不相关。
  } else {
    // 更新函数定义
    updateComponent = () => {
      // 首先执行vm._render(),不过我们并没有找到vm._render是做什么的,不过可以全局搜索可以知道,它的作用是生产虚拟dom
      // 然后执行_update函数将,同样不知道_update是做什么的,在同样的文件夹可以找到,它其实是执行了__patch_——方法,生产了真实dom
      vm._update(vm._render(), hydrating)
    }
  }
  // 挂载完成,页面有了真实dom
  // 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
  // 组件mounted的时候,创建watcher实例,所以每个组件都将至少有一个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
  // 此时,进行mounted相关的生命周期。
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}

vm._render函数做了些什么?在 /src/instance/render.js内,主要作用是生成虚拟dom vm._update函数做了些生命?在 src/core/instance/lifecycle

    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.
    // 初始化时没有prevVnode,则说明是首次挂载
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // 初始化时有prevVnode,则说明是更新函数,进行虚拟dom比较
      // updates
      // diff
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
  }

总结,$mount 主要是执行了mountComponent函数。 而在mountComponent函数中,

  1. 执行beforeMount钩子函数
  2. 先执行vm._render生产虚拟dom,然后执行vm._update,将虚拟dom转换为真实Dom,挂载
  3. 创建watcher观察者,响应式原理相关。
  4. 执行mounted钩子函数,并将isMounted置为true。

new Vue => 初始化数据与api,执行钩子函数beforeCreate,created => 执行$mount => beforeMount => _render,生产虚拟dom => _update, 真实dom挂载 => new Watcher => mounted钩子函数