美文网首页
Vue源码解析(五)之 mountComponent

Vue源码解析(五)之 mountComponent

作者: Love小六六 | 来源:发表于2019-08-21 11:16 被阅读0次

    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


    • 执行 get 函数,实际上调用了updateComponent


    • 也就是说执行了vm._update(vm._render(), hydrating)
    • vm._update 和 vm._render 又做了哪些事情呢?且听下回分解

    相关文章

      网友评论

          本文标题:Vue源码解析(五)之 mountComponent

          本文链接:https://www.haomeiwen.com/subject/qutbsctx.html