一步一步分析vue之$mount(1)

wangy7099發表於2019-02-25

前兩期,我們接連分析了_data,observe,接下來,我們來分析$mount。

$mount所做的工作從大體來講主要分為3步:

  1. 如果你的option裡面沒有render函式,那麼,通過compileToFunctions將HTML模板編譯成可以生成VNode的Render函式。
  2. new一個Watcher例項,觸發updateComponent方法。
  3. 生成vnode,經過patch,把vnode更新到dom上。
    由於篇幅有限,這裡先說前兩步,第三步下篇說。
    好,下面具體的說。首先,我們來到$mount函式,如下圖:
一步一步分析vue之$mount(1)

我們呢可以看到,程式碼首先判斷option裡面有沒有render函式,沒有的話,進一步判斷有沒有template,沒有的話就用dom元素的outerHTML。得到template以後幹什麼了呢?如下圖。

一步一步分析vue之$mount(1)

我們可以看到,呼叫了compileToFunctions將template轉成render函式。這裡面有兩個過程:

  • 將template解析成ast語法樹。
  • 通過ast語法樹生成render函式。

具體的將template解析成ast語法樹在本文就不說了,有時間單獨開一個章節分析。好,這下我們拿到render函式了,那麼接下來一步幹什麼了呢?沒錯,就開始mountComponent了。如下圖:

一步一步分析vue之$mount(1)

可以從上圖看到,程式宣告瞭一個updateComponent方法,這個是將要被Watcher例項呼叫的更新元件的方法,過一會分析到Watcher的時候將會看到。至於為什麼會有個判斷語句來根據條件宣告updateComponent方法,其實從performance可以看出,其中一個方法是用來測試renderupdate效能的。好我們終於該到Watcher了,先看這句程式碼:

// we set this to vm._watcher inside the watcher`s constructor
// since the watcher`s initial patch may call $forceUpdate (e.g. inside child
// component`s mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */);
複製程式碼

我們先來分析一下注釋裡所說的_watcher是啥玩意呢?其實看看forceupdate的程式碼就知道了:

  Vue.prototype.$forceUpdate = function () {
    var vm = this;
    if (vm._watcher) {
      vm._watcher.update();
    }
  };
複製程式碼

就是呼叫這個vm的_watcherupdate方法。用來強制更新。為什麼叫強制更新呢?vue裡面有判斷,如果新值 == 舊值, 那麼就不觸發watcher更新檢視了~ 所以,如果非要更新就要呼叫forceupdate來強制更新了。好,讓我們來看一看傳進去的引數吧:

  • vm:當前的vm例項
  • updateComponent 這個非常重要,用來在後面將vnode更新到dom上的。
  • noop 無意義的函式
  • null option選項,沒有則為null
  • true 主要是用來判斷是哪個watcher的。因為computed計算屬性和如果你要在options裡面配置watch了同樣也是使用了new Watcher,加上這個用以區別這三者。好,我們來看看new Watcher都做了什麼事,如下圖。
一步一步分析vue之$mount(1)

首先,我們看到程式碼有個這個判斷

if (isRenderWatcher) {
    vm._watcher = this;
}
複製程式碼

可以看到,如果宣告這個watcher的上下文是用來渲染檢視的,也就是說是在mountComponent這裡呼叫的new Watcher的時候,才會把this賦值給_watcher。然後把watcherpush到_watchers裡面,目的是等到元件銷燬時順便把watcher也銷燬掉。然後就是初始化watcher的成員,程式碼如下:

this.deep = this.user = this.lazy = this.sync = false;
複製程式碼

接下來,就是賦值給getter,this.getter = expOrFn。還記得剛才傳過來的updateComponent函式麼,沒錯,就是這個賦值給我getter。然後我們就到了:

this.value = this.lazy
    ? undefined
    : this.get();
複製程式碼

進入到get方法裡面,我們看看到底做了什麼。get程式碼如下圖:

一步一步分析vue之$mount(1)

我們可以看到,首先它執行的是pushTarget(this)pushTarget(this)程式碼如下:

function pushTarget (_target) {
  if (Dep.target) { targetStack.push(Dep.target); }
  Dep.target = _target;
}
複製程式碼

也就是說如果當前有Dep.target的話,就把target放到targetStack裡面,如果沒有的話,就設為當前的target,也就是這個watcher。
接著,就是執行了它的getter屬性,也就是剛剛傳入updateComponent函式。而updateComponent就是我們開篇提到第三步了。第三步我們下篇再說,如果你喜歡這篇文章請點個贊~ 謝謝! 您的支援也是作者的動力!並且如果有什麼問題,請在評論區進行提問,歡迎大家一起討論~

相關文章