Vue源码解析(五)之 mountComponent

Vue.prototype.$mount

  • 由上篇分析可知,我们将 render 函数挂载到 options 后调用了 mount 函数,这个函数在开始就定义了,实际上为Vue.prototype.$mount函数
  • 这个原型上的 $mount 函数从何而来呢,在platforms/web/runtime/index.js中有定义,我们会发现这个函数实际上是去调用了 mountComponent 方法
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
  • 看到这里你可能会很困惑,为什么会写两个 $mount 方法呢

实际上我们现在分析的是 with-compiler 的源码,即我们写模板文件然后通过entry-runtime-with-compiler将 template 编译成 render 函数再去执行runtime/index 中的 $mount 方法,如果不是 with-compiler 的 vue 的话 webpack 和 loader 就会处理将 template 编译成 render 函数,然后执行runtime/index 中的 $mount 方法,所以entry-runtime-with-compiler中的 $mount 方法实际上做的就是 webpack 和 loader 的事情,不过是在编译阶段处理的

mountComponent

  • core/instance/lifecycle.js中定义了 mountComponent 方法
export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode
  }
  callHook(vm, 'beforeMount')

  let updateComponent
  
  updateComponent = () => {
    vm._update(vm._render(), hydrating)
  }
  
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true)
  hydrating = false
  
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}
  • 这个方法先判断 vm.$options.render 是否存在,如果不存在的话就让它等于 createEmptyVNode
  • 接着定义了 updateComponent 函数
  • 创建了一个渲染 Watcher

Watcher

  • 我们在core/observer/watcher.js中查看 class Watcher
// 我们看下 Watcher 的构造函数
constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    if (options) {
      this.deep = !!options.deep
      this.user = !!options.user
      this.lazy = !!options.lazy
      this.sync = !!options.sync
      this.before = options.before
    } else {
      this.deep = this.user = this.lazy = this.sync = false
    }
    this.cb = cb
    this.id = ++uid 
    this.active = true
    this.dirty = this.lazy 
    this.deps = []
    this.newDeps = []
    this.depIds = new Set()
    this.newDepIds = new Set()
    this.expression = process.env.NODE_ENV !== 'production'
      ? expOrFn.toString()
      : ''
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get()
  }
  • 在 mountComponent 中 new Watcher 的传参
 vm: vm,
 expOrFn: updateComponent,
 cb: noop,
 options: 一个对象,
 isRenderWatcher: true
  • 分析得知vm._watcher = this,this.getter = updateComponent 最后执行了this.get()
  • get 函数 value = this.getter.call(vm, vm) 其实就是执行了updateComponent
get () {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }
  • 所以 mountCompnent 最主要就是实例化了渲染 Watcher 并调用了 updateComponent, 即 vm._update(vm._render(), hydrating)

demo 调试

  • 实例化渲染 Watcher


    Vue源码解析(五)之 mountComponent_第1张图片
  • 执行 get 函数,实际上调用了updateComponent


    Vue源码解析(五)之 mountComponent_第2张图片
  • 也就是说执行了vm._update(vm._render(), hydrating)
    Vue源码解析(五)之 mountComponent_第3张图片
  • vm._update 和 vm._render 又做了哪些事情呢?且听下回分解

你可能感兴趣的:(Vue源码解析(五)之 mountComponent)