VUE生命周期中的$mount掛載過程

一侈询、引子

在學習Vue的虛擬DOM的實現(xiàn)過程中,多次遇到“掛載”這個詞晚碾,偏偏在看到“掛載”這個詞的時候抓半,自己沒有辦法將這個詞對應的Vue生命周期具體的哪個部分,意識到自己對于生命周期的理解還是不夠深入格嘁,遂決定先加強學習一個$mount的這個過程笛求,查看了部分源碼以及擺渡了網(wǎng)上的很多文章,現(xiàn)做一點總結(jié)糕簿,下面這個簡約版的流程圖很短小精悍:
掛載的簡約版流程.png

在我第一次看到這個流程圖的時候探入,其實內(nèi)心是一臉蒙蔽,所以下決心去好好看一下里面的運行機制懂诗,下面是學習后對上面的流程圖進行補充之后所畫:
掛載原創(chuàng)流程.png

二蜂嗽、探究生命周期

在創(chuàng)建一個vue實例的時候(var vm = new Vue(options))。Vue的構(gòu)造函數(shù)將自動運行 this._init(啟動函數(shù))殃恒。啟動函數(shù)的最后一步為initRender(vm):

// Vue.prototype._init
    ...
    initLifecycle(vm);
    initEvents(vm);
    callHook(vm, 'beforeCreate');
    initState(vm);
    callHook(vm, 'created');
    initRender(vm);

initRender中調(diào)用vm.mount(vm.options.el):

//initRender
  ...
  if (vm.$options.el) {
    vm.$mount(vm.$options.el);
  }

將實例掛載到dom上植旧,至此啟動函數(shù)完成。

若Vue實例上面沒有el屬性离唐,則生命周期執(zhí)行到這就掛起了病附,直到手動去執(zhí)行vm.mount(el),生命周期才會繼續(xù)執(zhí)行亥鬓,接下來的重點就是mount這個方法究竟完成了哪些事情完沪,這就是我們需要重點關注的。

三嵌戈、$mount函數(shù)的學習

當你去尋找mount函數(shù)的時候覆积,你會發(fā)現(xiàn)不止一個听皿,具體的mount的實現(xiàn)和平臺、環(huán)境都有關系宽档,主要是依據(jù)構(gòu)建方式區(qū)分為下面兩種:

(1)獨立構(gòu)建:包含模板編譯器

? 渲染過程: html字符串 → render函數(shù) → vnode → 真實dom節(jié)點

(2)運行時構(gòu)建: 不包含模板編譯器

? 渲染過程: render函數(shù) → vnode → 真實dom節(jié)點

可以看一下官網(wǎng)中的解釋:運行時 + 編譯器 vs. 只包含運行時

// 需要編譯器
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 不需要編譯器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

3.1 運行時+編譯器的$mount函數(shù)

學習的重點就是有編譯器的mount函數(shù)写穴,該函數(shù)其實就是在運行時的Vue實例原型上面的mount函數(shù)上面做了一層包裝,首先限制 el 不能為 body雌贱、html 這類根節(jié)點啊送,接著,檢查是否有 render 方法欣孤,如果沒有則會把 el 或者 template 字符串轉(zhuǎn)換成 render 方法馋没,最后調(diào)用 compileToFunctions 方法實現(xiàn)render在線編譯,在確保已經(jīng)存在render函數(shù)的情況下降传,最后還是調(diào)用原型上面的mount函數(shù)篷朵。

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

  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    ...
      const { render, staticRenderFns } = compileToFunctions(template, {
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns
    ...
  }
  return mount.call(this, el, hydrating)
}

3.2 原型上面的$mount函數(shù)

原型的$mount函數(shù)中最重要的是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
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
  }
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false

  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}

mountComponent 先調(diào)用 vm._render 方法先生成虛擬 Node,再實例化一個Watcher婆排,由此看出声旺,渲染最核心的 2 個方法:vm._rendervm._update

Vue 的 _render 方法是實例的一個私有方法段只,可以把實例渲染成一個虛擬 Node腮猖,定義在 src/core/instance/render.js 中。平時開發(fā)工作中很少手寫 render 赞枕,大多是寫 template 模板澈缺,在上面的 mounted 方法中會把 template 編譯成 render 方法

Vue 的 _update 是實例的私有方法,它只在首次渲染和數(shù)據(jù)更新兩種情況下被調(diào)用炕婶,_update 方法把 VNode 渲染成真實的 DOM

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
  const vm: Component = this
  const prevEl = vm.$el
  const prevVnode = vm._vnode
  const prevActiveInstance = activeInstance
  activeInstance = vm
  vm._vnode = vnode
  if (!prevVnode) {
    vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
  } else {
    vm.$el = vm.__patch__(prevVnode, vnode)
  }
  activeInstance = prevActiveInstance
  if (prevEl) {
    prevEl.__vue__ = null
  }
  if (vm.$el) {
    vm.$el.__vue__ = vm
  }
  if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
    vm.$parent.$el = vm.$el
  }
}

這方法中最終實現(xiàn)將真實的DOM節(jié)點掛載到vm.$el上面姐赡,至此VUE生命周期中的掛載過程結(jié)束。

四柠掂、生命周期回顧

由上面的所有描述项滑,最終可以得到一個較為完整但是很精簡的流程圖:

精簡流程.png

最后附上一份VUE的生命周期圖,對照著整個掛載過程涯贞,一切都很清晰枪狂。

最最后,大家覺得有什么問題肩狂,或者有什么疑惑惡意給我留言摘完,覺得有所幫助的,記得點個贊哦I邓!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末列粪,一起剝皮案震驚了整個濱河市审磁,隨后出現(xiàn)的幾起案子谈飒,更是在濱河造成了極大的恐慌,老刑警劉巖态蒂,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杭措,死亡現(xiàn)場離奇詭異,居然都是意外死亡钾恢,警方通過查閱死者的電腦和手機手素,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘩蚪,“玉大人泉懦,你說我怎么就攤上這事≌钍荩” “怎么了崩哩?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵泉褐,是天一觀的道長暮顺。 經(jīng)常有香客問我,道長擅笔,這世上最難降的妖魔是什么险胰? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任汹押,我火速辦了婚禮,結(jié)果婚禮上起便,老公的妹妹穿的比我還像新娘鲸阻。我一直安慰自己,他們只是感情好缨睡,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布鸟悴。 她就那樣靜靜地躺著,像睡著了一般奖年。 火紅的嫁衣襯著肌膚如雪细诸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天陋守,我揣著相機與錄音震贵,去河邊找鬼。 笑死水评,一個胖子當著我的面吹牛猩系,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播中燥,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼寇甸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拿霉,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吟秩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绽淘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涵防,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年沪铭,在試婚紗的時候發(fā)現(xiàn)自己被綠了壮池。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡杀怠,死狀恐怖椰憋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情驮肉,我是刑警寧澤熏矿,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站离钝,受9級特大地震影響票编,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卵渴,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一慧域、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浪读,春花似錦昔榴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至痘拆,卻和暖如春仰禽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纺蛆。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工吐葵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桥氏。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓温峭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親字支。 傳聞我的和親對象是個殘疾皇子凤藏,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容