前兩期,我們接連分析了_data,observe,接下來,我們來分析$mount。
$mount所做的工作從大體來講主要分為3步:
- 如果你的option裡面沒有
render
函式,那麼,通過compileToFunctions
將HTML模板編譯成可以生成VNode的Render函式。 new
一個Watcher
例項,觸發updateComponent
方法。- 生成vnode,經過patch,把vnode更新到dom上。
由於篇幅有限,這裡先說前兩步,第三步下篇說。
好,下面具體的說。首先,我們來到$mount
函式,如下圖:
我們呢可以看到,程式碼首先判斷option裡面有沒有render函式,沒有的話,進一步判斷有沒有template,沒有的話就用dom元素的outerHTML。得到template以後幹什麼了呢?如下圖。
我們可以看到,呼叫了compileToFunctions
將template轉成render函式。這裡面有兩個過程:
- 將template解析成ast語法樹。
- 通過ast語法樹生成render函式。
具體的將template解析成ast語法樹在本文就不說了,有時間單獨開一個章節分析。好,這下我們拿到render函式了,那麼接下來一步幹什麼了呢?沒錯,就開始mountComponent
了。如下圖:
可以從上圖看到,程式宣告瞭一個updateComponent
方法,這個是將要被Watcher
例項呼叫的更新元件的方法,過一會分析到Watcher
的時候將會看到。至於為什麼會有個判斷語句來根據條件宣告updateComponent
方法,其實從performance
可以看出,其中一個方法是用來測試render
和update
效能的。好我們終於該到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的_watcher
的update
方法。用來強制更新。為什麼叫強制更新呢?vue裡面有判斷,如果新值 == 舊值, 那麼就不觸發watcher更新檢視了~ 所以,如果非要更新就要呼叫forceupdate
來強制更新了。好,讓我們來看一看傳進去的引數吧:
- vm:當前的vm例項
- updateComponent 這個非常重要,用來在後面將vnode更新到dom上的。
- noop 無意義的函式
- null option選項,沒有則為null
- true 主要是用來判斷是哪個watcher的。因為computed計算屬性和如果你要在options裡面配置watch了同樣也是使用了
new Watcher
,加上這個用以區別這三者。好,我們來看看new Watcher
都做了什麼事,如下圖。
首先,我們看到程式碼有個這個判斷
if (isRenderWatcher) {
vm._watcher = this;
}
複製程式碼
可以看到,如果宣告這個watcher的上下文是用來渲染檢視的,也就是說是在mountComponent
這裡呼叫的new Watcher
的時候,才會把this賦值給_watcher。然後把watcher
push到_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程式碼如下圖:
我們可以看到,首先它執行的是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
就是我們開篇提到第三步了。第三步我們下篇再說,如果你喜歡這篇文章請點個贊~ 謝謝! 您的支援也是作者的動力!並且如果有什麼問題,請在評論區進行提問,歡迎大家一起討論~