vue源碼解析-$mount

上一篇中,我們一起探討了new Vue({...})背后發(fā)生了什么嚷量。那么當(dāng)我們實(shí)例化vue之后窒盐,進(jìn)行dom掛載又發(fā)生了什么呢糊肤?

image.png

細(xì)心的同學(xué)會(huì)發(fā)現(xiàn):$mount方法在多個(gè)文件中被定義巴席,如:

  • src/platform/web/entry-runtime-with-compiler.js
  • src/platform/web/runtime/index.js
  • src/platform/weex/runtime/index.js

之所以有多個(gè)地方漾峡,是因?yàn)?mount實(shí)現(xiàn)是和平臺(tái)竹握、構(gòu)建方式都相關(guān)的
下面葱淳,我們選擇compiler版本分析

一. $mount 主干代碼如下:

Vue.prototype.$mount = function(el?: string | Element, hydrating?: boolean): Component {
  el = el && query(el)
  // query方法,實(shí)際上是對(duì)el參數(shù)做了一個(gè)轉(zhuǎn)化谦铃,el可能是string 或者 element耘成。如果是string,將返回document.querySelector(el) 
  // ...
  const options = this.$options

  if (!options.render) {
    // render函數(shù)不存在
    let template = options.template
    
    if (template) {
       // 如果存在template配置項(xiàng):
       // 1. template可能是"#xx"驹闰,那么根據(jù)id獲取element內(nèi)容
       // 2. 如果template存在nodeType瘪菌,那么獲取template.innerHTML 內(nèi)容
    }else {
       // 如果template配置項(xiàng)不存在template,但是存在el:
      /*  
       * 例如: new Vue({ 
       *       el: "#app",
      *       ...
       *  })
       *
       */
      // 那么根據(jù)el獲取對(duì)應(yīng)的element內(nèi)容
    }

    // 經(jīng)過上面的處理嘹朗,將獲取的template做為參數(shù)調(diào)用compileToFunctions方法
    // compileToFunctions方法會(huì)返回render函數(shù)方法师妙,render方法會(huì)保存到vm.$options下面
    const { render, staticRenderFns } = compileToFunctions(template, {...})
    options.render = render
  }
  return mount.call(this, el, hydrating)
}

從主干代碼我們可以看出做了以下幾件事

  • 由于el參數(shù)有兩種類型,可能是string 或者 element骡显,調(diào)用query方法疆栏,統(tǒng)一轉(zhuǎn)化為Element類型
  • 如果沒有手寫render函數(shù), 那么先獲取template內(nèi)容惫谤。再將template做為參數(shù)壁顶,調(diào)用compileToFunctions方法,返回render函數(shù)溜歪。
  • 最后調(diào)用mount.call若专,這個(gè)方法實(shí)際上會(huì)調(diào)用runtime/index.js的mount方法

注:

  1. vue compiler分別2個(gè)版本:一個(gè)是構(gòu)建時(shí)版本,即我們使用vue-loader + webpack蝴猪。另一個(gè)版本是:運(yùn)行時(shí)版本调衰,運(yùn)行的時(shí)候,再去compiler解析自阱。我們這里分析的是 運(yùn)行時(shí)
  2. vue最終只認(rèn)render函數(shù)嚎莉,所以如果我們手動(dòng)寫render函數(shù),那么就直接調(diào)用mount.call沛豌。反之趋箩,vue會(huì)將template做為參數(shù),運(yùn)行時(shí)調(diào)用compileToFunctions方法加派,轉(zhuǎn)化為render函數(shù)叫确,再去調(diào)用mount.call方法。
  3. 如果是構(gòu)建時(shí)版本芍锦,vue-loader + webpack竹勉,會(huì)先將我們本地的代碼轉(zhuǎn)化成render函數(shù),運(yùn)行將直接調(diào)用mount.call娄琉。生產(chǎn)環(huán)境次乓,我們推薦構(gòu)建時(shí)的版本。個(gè)人學(xué)習(xí)推薦運(yùn)行時(shí)版本车胡。
  4. mount.call方法檬输,實(shí)際上會(huì)調(diào)用runtime/index.js下面的$mount方法,而這個(gè)方法很簡(jiǎn)單匈棘,將會(huì)調(diào)用mountComponent方法丧慈。

二. mountComponent 主干代碼如下:

export function mountComponent(vm: Component, el: ?Element, hydrating?: boolean): Component {
  // ...
  // 調(diào)用beforeMount生命周期函數(shù)
  callHook(vm, 'beforeMount')
  // ...
  // 定義updateComonent方法
  let updateComponent = () => {
      vm._update(vm._render(), hydrating)
  }
  
  // ...
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true)
  // ...
  // 調(diào)用生命周期函數(shù)mounted
  callHook(vm, 'mounted')
}

Watch類相關(guān)代碼

Watch類有許多邏輯,這里我們只看和$mount相關(guān)的:

class Watcher {
    constructor(
        vm: Component,
        expOrFn: string | Function,
        cb: Function,
        options?: ?Object,
        isRenderWatcher?: boolean
    ){
        // ...
        if (typeof expOrFn === 'function') {
          this.getter = expOrFn
        }else {
          // ...
        }

        // ...
        this.value = this.lazy ? undefined : this.get()
    }

    get() {
      // ...
      value = this.getter.call(vm, vm)
      // ...
      // cleanupDeps方法后面我們會(huì)分析主卫,這個(gè)在性能優(yōu)化上比較重要
      return value;
    }
}

從上面代碼逃默,可以看出:

  • 先調(diào)用beforeMount鉤子函數(shù)
  • 將updateComponent方法做為參數(shù),實(shí)例化Watch簇搅。Watch在這個(gè)有2個(gè)作用:
    1完域、初始化的時(shí)候會(huì)執(zhí)行回調(diào)函數(shù)
    2、當(dāng) vm 實(shí)例中的監(jiān)測(cè)的數(shù)據(jù)發(fā)生變化的時(shí)候執(zhí)行回調(diào)函數(shù)
    這里瘩将,我們先看第1個(gè)吟税。 第2個(gè)將在數(shù)據(jù)變化監(jiān)測(cè)章節(jié)分析
    執(zhí)行回調(diào)后凹耙,我們看到vm._update(vm._render(), hydrating)方法,這個(gè)方法分2個(gè)步驟:
    (1) 執(zhí)行render方法肠仪,返回最新的 VNode節(jié)點(diǎn)樹
    (2) 調(diào)用update方法肖抱,實(shí)際上進(jìn)行diff算法比較,完成一次渲染
  • 調(diào)用mounted鉤子函數(shù)

三. 總結(jié)

  1. options上無render函數(shù)异旧,對(duì)template, el做處理意述,獲取template內(nèi)容。
  2. 調(diào)用compileToFunctions方法吮蛹,獲取render函數(shù)荤崇,添加到options.render上
  3. 調(diào)用mount.call,實(shí)際上是調(diào)用mountComponent函數(shù)
  4. 調(diào)用beforeMount鉤子
  5. 實(shí)例化渲染watcher潮针,執(zhí)行回調(diào)
  6. 根據(jù)render函數(shù)獲取VNode節(jié)點(diǎn)樹 (其實(shí)是一個(gè)js對(duì)象)
  7. 執(zhí)行update方法术荤,實(shí)際上是patch過程,vue會(huì)執(zhí)行diff算法每篷,完成一次渲染
  8. 調(diào)用mounted鉤子

在下面的章節(jié)喜每,我們將陸續(xù)分析: 響應(yīng)式,compileToFunctions, 虛擬DOM雳攘,以及patch
碼字不易带兜,多多關(guān)注~??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吨灭,隨后出現(xiàn)的幾起案子刚照,更是在濱河造成了極大的恐慌,老刑警劉巖喧兄,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件无畔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吠冤,警方通過查閱死者的電腦和手機(jī)浑彰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拯辙,“玉大人郭变,你說我怎么就攤上這事⊙谋#” “怎么了诉濒?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)夕春。 經(jīng)常有香客問我未荒,道長(zhǎng),這世上最難降的妖魔是什么及志? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任片排,我火速辦了婚禮寨腔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘率寡。我一直安慰自己脆侮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布勇劣。 她就那樣靜靜地躺著,像睡著了一般潭枣。 火紅的嫁衣襯著肌膚如雪比默。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天盆犁,我揣著相機(jī)與錄音命咐,去河邊找鬼。 笑死谐岁,一個(gè)胖子當(dāng)著我的面吹牛醋奠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伊佃,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼窜司,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了航揉?” 一聲冷哼從身側(cè)響起塞祈,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帅涂,沒想到半個(gè)月后议薪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡媳友,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年斯议,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片醇锚。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哼御,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出焊唬,到底是詐尸還是另有隱情艇搀,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布求晶,位于F島的核電站焰雕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芳杏。R本人自食惡果不足惜矩屁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一辟宗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吝秕,春花似錦泊脐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至约郁,卻和暖如春缩挑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鬓梅。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工供置, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绽快。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓芥丧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親坊罢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子续担,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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