Vue 首次渲染源码

  • 在初始化结束会调用 $mount进行页面挂载
Vue.prototype._init = function (options?: Object) {
  ...
  if (vm.$options.el) {
    // 挂载页面
    vm.$mount(vm.$options.el)
  }
}
  • 如果是带编译器的版本,会跳转到 $mount的重写方法,多了将模板编译成render函数的操作,详见
// # platforms/web/entry-runtime-with-compiler.js

Vue.prototype.$mount = function (){
  ...
  // 调用 web/runtime/index.js下定义的$mount 方法
  return mount.call(this, el, hydrating)
}
  • 原始定义的 $mount方法,处理了el,并调用了 mountComponent
// # platforms/web/runtime/index.js

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {

  // 为什么这里 && 一下?
  // 如果带编译器版本,会重写 $mount方法,此时el在重写方法内已经初始化
  // 如果不带编译器版本,浏览器环境下,需要把el转化成 dom,获取dom.innerHTML作为模板

  el = el && inBrowser ? query(el) : undefined

  return mountComponent(this, el, hydrating)
}
  • mountComponent方法定义在 core/instance/lifecycle.js

mountComponent方法解析

  • 首先判断 如果开发模式没render函数,且使用运行时版本,则template模板会报错
  • 然后执行 callHook(vm,'beforeMount') 钩子
  • 然后定义 updateComponent方法,挂载操作的方法,(此处只是定义该方法并未执行)
updateComponent = () => {

  // _render 调用用户传入的render或编译器生成的render,返回VNode虚拟dom
  // _update将虚拟dom转为真实dom

  vm._update(vm._render(), hydrating)
}
  • 调用 updateComponent方法在 Watcher实例中
new Watcher(vm, updateComponent, noop, {
  before () {
    if (vm._isMounted && !vm._isDestroyed) {
      callHook(vm, 'beforeUpdate')
    }
  }
}, true /* isRenderWatcher */)
  • 最后执行 callHook(vm,"mounted") 钩子

Watcher类解析

  • Watcher类写在 core/observer/watcher.js
  • 源码watcher类会调用三次 渲染watcher(当前),计算属性watcher,侦听器watcher
  • Watcher类中定义很多属性,lazy属性true 用于计算属性watcher
...
get(){

  // 每个组件对应一个watcher
  // 如果组件有嵌套,先渲染内部组件,需要先把父组件watcher保存起来
  pushTarget(this)

  // 这里的 getter就是 传入的 updataComponent
  value = this.getter.call(vm, vm)
}
...

updateComponent解析

  • 此方法在 core/instance/lifecycle.js下(之前注册的),执行完则首次渲染完毕
updateComponent = () => {
  // 这是个回调,wather里面会调用
  // _render 或获取虚拟dom  _update将虚拟dom转为真实dom
  vm._update(vm._render(), hydrating)
}

mcgee0731
60 声望4 粉丝

不会做饭的程序猿不是一个好厨子