Vue渲染原理

現在基本所有的框架都已經認同這個看法——DOM應儘可能是一個函數式到狀態的映射。狀態即是唯一的真相,而DOM狀態只是數據狀態的一個映射。如下圖所示,所有的邏輯儘可能在狀態的層面去進行,當狀態改變的時候,View應該是在框架幫助下自動更新到合理的狀態,而不是說當你觀測到數據變化之後手動選擇一個元素,再命令式地去改動它的屬性。

下圖是Vue的一個模板示例,如果沒有用過Vue的話,可以大概感覺到這是一個怎樣的概念。

其實,在模板語法上,Vue跟Angular是比較相似。在Vue1.0裏面,模板實現跟Angular類似,如下圖所示,把模板直接做成在瀏覽器裏面parse成DOM樹,然後去遍歷這個樹,提取其中的各種綁定。

在Vue2.0中,渲染層的實現做了根本性改動,那就是引入了虛擬DOM。

從架構來講,Vue2.0 依然是寫一樣的模板,(Vue2.0於前段時間發佈,具體報道:更輕更快的Vue.js 2.0)。在最左邊,Vue2.0跟1.0的模板語法絕大部分是兼容的。Vue的編譯器在編譯模板之後,會把這些模板編譯成一個渲染函數。而函數被調用的時候就會渲染並且返回一個虛擬DOM的樹。這個樹非常輕量,它的職責就是描述當前界面所應處的狀態。當我們有了這個虛擬的樹之後,再交給一個patch函數,負責把這些虛擬DOM真正施加到真實的DOM上。在這個過程中,Vue有自身的響應式系統來偵測在渲染過程中所依賴到的數據來源。在渲染過程中,偵測到的數據來源之後,之後就可以精確感知數據源的變動。到時候就可以根據需要重新進行渲染。當重新進行渲染之後,會生成一個新的樹,將新樹與舊樹進行對比,就可以最終得出應施加到真實DOM上的改動。最後再通過patch函數施加改動。

這樣做的主要原因是,在瀏覽器當中,JavaScript的運算在現代的引擎中非常快,但DOM本身是非常緩慢的東西。當你調用原生DOM API的時候,瀏覽器需要在JavaScript引擎的語境下去接觸原生的DOM的實現,這個過程有相當的性能損耗。所以,本質的考量是,要把耗費時間的操作儘量放在純粹的計算中去做,保證最後計算出來的需要實際接觸真實DOM的操作是最少的。

下面看渲染函數。用過React的開發者可能知道,React是沒有模板的,直接就是一個渲染函數,它中間返回的就是一個虛擬DOM樹。JSX實際就是一套用於讓我們更簡單地去描述樹狀結構的語法糖。

如下圖所示,在Vue2.0當中,可以看到就是說當比如左側的模板,經過Vue的編譯之後就會變成右側的東西。

這個函數類似於創建一個虛擬元素的函數,我們可以給它一個名字,給它描述應該有的屬性特性和可能其他的數據。然後後面這個最後這個參數是個數組,包含了該虛擬元素的子元素。總的來說2.0的編譯器做的就是這個活。

同時,在Vue2.0裏,用戶可以選擇直接跳過模板這一層去手寫渲染函數,同時也有可選JSX支持。從開發者的偏好以及開發者的效益的角度來考量,模板和JSX是各有利弊的東西。模板更貼近我們的HTML,可以讓我們更直觀地思考語義結構,更好地結合CSS的書寫。JSX和直接渲染函數,因爲是真正的JavaScript,擁有這個語言本身的所有的能力,可以進行復雜的邏輯判斷,進行選擇性的返回最終要返回的DOM結構,能夠實現一些在模板的語法限制下,很難做到的一些事情。

所以在Vue2.0裏,兩個都是可以選擇的。在絕大部分情況下使用模板,但是在需要複雜邏輯的情況下,使用渲染函數。在Vue2.0的路由和內部的一些實踐上,都大量地應用渲染函數做複雜的抽象組件,比如過渡動畫組件以及路由裏面的link組件,都是用渲染函數實現的,同時還保留了它本身的依賴追蹤系統。

如下圖所示,Vue的依賴追蹤通過ES5的 Object.defineProperty 方法實現。比如,我們給它一個原生對象,Vue會遍歷這個數據對象的屬性,然後進行屬性轉換。每一個屬性會被轉換爲一個 getter 和一個 setter。同時每個組件會有一個對應的 watcher 對象,這個對象的職責就是在當前組件被渲染的時候,記錄數據上面的哪些屬性被用到了。

例如,在渲染函數裏面用到A.B的時候,這個就會觸發對應的 getter。整個渲染流程具體要點如下:

  • 當某個數據屬性被用到時,觸發 getter,這個屬性就會被作爲依賴被 watcher 記錄下來。

  • 整個函數被渲染完的時候,每一個被用到的數據屬性都會被記錄。

  • 相應的數據變動時,例如給它一個新的值,就會觸發 setter,通知數據對象對應數據有變化。

  • 此時會通知對應的組件,其數據依賴有所改動,需要重新渲染。

  • 對應的組件再次調動渲染函數,生成 Virtual DOM,實現 DOM 更新。

這樣一個流程跟主流的一些框架,例如React是有較大區別的。在React中,當組件複雜的時候需要用 shouldComponentUpdate 做優化。但是,它也有自己的各種坑,比如要確保該組件下面的組件不依賴外部的狀態。雖說這在大部分情況下是夠用的,但遇到極大複雜度的應用,遇到性能瓶頸的時候,這個流程優化起來也是相當複雜的一個話題。

如下圖所示,在Vue裏面由於依賴追蹤系統的存在,當任意數據變動的時,Vue的每一個組件都精確地知道自己是否需要重繪,所以並不需要手動優化。用Vue渲染這些組件的時候,數據變了,對應的組件基本上去除了手動優化的必要性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章