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


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


  • 也就是说执行了vm._update(vm._render(), hydrating)
  • vm._update 和 vm._render 又做了哪些事情呢?且听下回分解
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • vue 入口 从vue的构建过程可以知道,web环境下,入口文件在 src/platforms/web/entry...
    黄呼呼爱编程阅读 763评论 0 0
  • Vue 是通过$mount实例方法去挂载vm的,$mount方法在多个文件中都有定义,如src/platform/...
    famingng阅读 7,124评论 0 3
  • 源码版本:v2.1.10 分析目标 通过阅读源码,对 Vue2 的基础运行机制有所了解,主要是: Vue2 中数据...
    NARUTO_86阅读 12,241评论 6 26
  • 因为最近我们组内有个分享主题,即vue2的源码学习分享,我们几个人分别分享几个不同部分,但是虽然我们的分工是每个人...
    阿go阅读 1,727评论 0 0
  • 昨天夜里三四点起来喝水,脑子里突然有很多的灵感和火花。炸的呀,我就直接坐在椅子上靠那了。 最近读了一本好书看了一部...
    赵小艳_18e7阅读 255评论 0 2