虛擬dom,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 到底要解決哪些事情。

  1. 將頁面改變的內容應用到虛擬 DOM 上,而不是直接應用到 DOM 上。
  2. 變化被應用到虛擬 DOM 上時,虛擬 DOM 并不急著去渲染頁面,而僅僅是調整虛擬 DOM 的內部狀態,這樣操作虛擬 DOM 的代價就變得非常輕了。
  3. 在虛擬 DOM 收集到足夠的改變時,再把這些變化一次性應用到真實的 DOM 上。

基于以上三點,我們再來看看什么是虛擬 DOM。為了直觀理解,你可以參考下圖:

image.png

該圖是結合 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 的基礎結構,你可以先參考下圖:

image.png

通過上圖可以發現,MVC 的整體結構比較簡單,由模型、視圖和控制器組成,其核心思想就是將數據和視圖分離,也就是說視圖和模型之間是不允許直接通信的,它們之間的通信是通過控制器來完成的。
比如在分析 React 項目時,我們可以把 React 的部分看成是一個 MVC 中的視圖,在項目中結合 Redux 就可以構建一個 MVC 的模型結構,如下圖所示:

image.png

在該圖中,我們可以把虛擬 DOM 看成是 MVC 的視圖部分,其控制器和模型都是由 Redux 提供的。其具體實現過程如下:

  • 圖中的控制器是用來監控 DOM 的變化,一旦 DOM 發生變化,控制器便會通知模型,讓其更新數據。
  • 模型數據更新好之后,控制器會通知視圖,告訴它模型的數據發生了變化。
  • 視圖接收到更新消息之后,會根據模型所提供的數據來生成新的虛擬 DOM。
  • 新的虛擬 DOM 生成好之后,就需要與之前的虛擬 DOM 進行比較,找出變化的節點。
  • 比較出變化的節點之后,React 將變化的虛擬節點應用到 DOM 上,這樣就會觸發 DOM 節點的更新。
  • DOM 節點的變化又會觸發后續一系列渲染流水線的變化,從而實現頁面的更新。

在實際工程項目中,你需要學會分析出各個模塊,并梳理出它們之間的通信關系,這樣對于任何框架你都能輕松上手了。

參考來源

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容