這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 本文我們會先聊聊 DOM 的一些缺陷,然後在此基礎上介紹虛擬 DOM 是如何解決這些缺陷的,最後再站在雙緩存和 MVC 的視角來聊聊虛擬 DOM。理解了這些會讓你對目前的前端框架有一個更加底層的認識,這也有助於你更好地理解這些前端框 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
本文我們會先聊聊 DOM 的一些缺陷,然後在此基礎上介紹虛擬 DOM 是如何解決這些缺陷的,最後再站在雙緩存和 MVC 的視角來聊聊虛擬 DOM。理解了這些會讓你對目前的前端框架有一個更加底層的認識,這也有助於你更好地理解這些前端框架。
DOM 的缺陷
比如,我們可以調用 document.body.appendChild(node)
往 body
節點上添加一個元素,調用該 API 之後會引發一系列的連鎖反應。首先渲染引擎會將 node
節點添加到 body
節點之上,然後觸發樣式計算、佈局、繪製、柵格化、合成等任務,我們把這一過程稱為 重排。除了重排之外,還有可能引起重繪或者合成操作,形象地理解就是“牽一發而動全身”。另外,對於 DOM 的不當操作還有可能引發強制同步佈局和佈局抖動的問題,這些操作都會大大降低渲染效率。因此,對於 DOM 的操作時我們需要非常謹慎。
對於一些複雜的頁面或者目前使用非常多的單頁應用來說,其 DOM 結構是非常複雜的,而且還需要不斷地去修改 DOM 樹,每次操作 DOM 渲染引擎都需要進行重排、重繪或者合成等操作,因為 DOM 結構複雜,所生成的頁面結構也會很複雜,對於這些複雜的頁面,執行一次重排或者重繪操作都是非常耗時的,這就給我們帶來了真正的性能問題。所以我們需要有一種方式來減少 JavaScript 對 DOM 的操作,這時候虛擬 DOM 就上場了。
什麼是虛擬 DOM?
在談論什麼是虛擬 DOM 之前,我們先來看看虛擬 DOM 到底要解決哪些事情。
- 將頁面改變的內容應用到虛擬 DOM 上,而不是直接應用到 DOM 上。
- 變化被應用到虛擬 DOM 上時,虛擬 DOM 並不急著去渲染頁面,而僅僅是調整虛擬 DOM 的內部狀態,這樣操作虛擬 DOM 的代價就變得非常輕了。
- 在虛擬 DOM 收集到足夠的改變時,再把這些變化一次性應用到真實的 DOM 上。
基於以上三點,我們再來看看什麼是虛擬 DOM。為了直觀理解,你可以參考下圖:
該圖是結合 React 流程畫的一張虛擬 DOM 執行流程圖,下麵我們就結合這張圖來分析下虛擬 DOM 到底怎麼運行的。
- 創建階段。首先依據 JSX 和基礎數據創建出來虛擬 DOM,它反映了真實的 DOM 樹的結構。然後由虛擬 DOM 樹創建出真實 DOM 樹,真實的 DOM 樹生成完後,再觸發渲染流水線往屏幕輸出頁面。
- 更新階段。如果數據發生了改變,那麼就需要根據新的數據創建一個新的虛擬 DOM 樹;然後 React 比較兩個樹,找出變化的地方,並把變化的地方一次性更新到真實的 DOM 樹上;最後渲染引擎更新渲染流水線,並生成新的頁面。
既然聊到虛擬 DOM 的更新,那我們就不得不聊聊最新的 React Fiber 更新機制了。最開始的時候,比較兩個虛擬 DOM 的過程是在一個遞歸函數里執行的,其核心演算法是 reconciliation。通常情況下,這個比較過程執行得很快,不過當虛擬 DOM 比較複雜的時候,執行比較函數就有可能占據主線程比較久的時間,這樣就會導致其他任務的等待,造成頁面卡頓。
為瞭解決這個問題,React 團隊重寫了 reconciliation 演算法,新的演算法稱為 Fiber reconciler,所謂的 Fiber reconciler,就是在執行演算法的過程中出讓主線程,這樣就解決了之前執行函數占用時間過久的問題。至於具體的實現過程在這裡就不詳細分析了,如果感興趣的話,你可以自行查閱相關資料進行學習。
瞭解完虛擬 DOM 的大致執行流程,你應該也就知道為何需要虛擬 DOM 了。不過以上都從單純的技術視角來分析虛擬 DOM 的,那接下來我們再從雙緩存和 MVC 模型這兩個視角來聊聊虛擬 DOM。
雙緩存
使用雙緩存,可以讓你先將計算的中間結果存放在另一個緩衝區中,等全部的計算結束,該緩衝區已經存儲了完整的圖形之後,再將該緩衝區的圖形數據一次性複製到顯示緩衝區,這樣就使得整個圖像的輸出非常穩定。
在這裡,你可以把虛擬 DOM 看成是 DOM 的一個 buffer,和圖形顯示一樣,它會在完成一次完整的操作之後,再把結果應用到 DOM 上,這樣就能減少一些不必要的更新,同時還能保證 DOM 的穩定輸出。
MVC 模式
接下來我們再來看看虛擬 DOM 在 MVC 模式中所扮演的角色。
在各大設計模式當中,MVC 是一個非常重要且應用廣泛的模式,因為它能將數據和視圖進行分離,在涉及到一些複雜的項目時,能夠大大減輕項目的耦合度,使得程式易於維護。關於 MVC 的基礎結構,你可以先參考下圖:
通過上圖可以發現,MVC 的整體結構比較簡單,由模型、視圖和控制器組成,其核心思想就是將數據和視圖分離,也就是說視圖和模型之間是不允許直接通信的,它們之間的通信是通過控制器來完成的。
比如在分析 React 項目時,我們可以把 React 的部分看成是一個 MVC 中的視圖,在項目中結合 Redux 就可以構建一個 MVC 的模型結構,如下圖所示:
在該圖中,我們可以把虛擬 DOM 看成是 MVC 的視圖部分,其控制器和模型都是由 Redux 提供的。其具體實現過程如下:
- 圖中的控制器是用來監控 DOM 的變化,一旦 DOM 發生變化,控制器便會通知模型,讓其更新數據。
- 模型數據更新好之後,控制器會通知視圖,告訴它模型的數據發生了變化。
- 視圖接收到更新消息之後,會根據模型所提供的數據來生成新的虛擬 DOM。
- 新的虛擬 DOM 生成好之後,就需要與之前的虛擬 DOM 進行比較,找出變化的節點。
- 比較出變化的節點之後,React 將變化的虛擬節點應用到 DOM 上,這樣就會觸發 DOM 節點的更新。
- DOM 節點的變化又會觸發後續一系列渲染流水線的變化,從而實現頁面的更新。
在實際工程項目中,你需要學會分析出各個模塊,並梳理出它們之間的通信關係,這樣對於任何框架你都能輕鬆上手了。