解析 React 虛擬 DOM 原理 + DOM Diff 算法

導讀:

  • 當用原生 JS / jQuery 操作 DOM 時,瀏覽器會從構(gòu)建 DOM 樹開始從頭到尾渲染一遍 DOM 節(jié)點,而大量的 DOM 操作會頻繁的更新 DOM( 即再次從頭到尾渲染 ),這無疑是非常消耗瀏覽器性能的,因此由 FaceBook 團隊提出的虛擬 DOM 橫空出世,并運用在了 React 之上( 目前 Vue 2.x 也引入了這個概念 ),接下來我們就分析一下虛擬 DOM 的原理。

虛擬 DOM 原理:

  • 無虛擬 DOM 時,React 中 DOM 的渲染流程:
    1. state 數(shù)據(jù)
    2. JSX 模板
    3. 數(shù)據(jù) + 模板 結(jié)合,生成真實的 DOM,來顯示
    4. state 發(fā)生改變
    5. 數(shù)據(jù) + 模板 結(jié)合,生成真實的 DOM,替換原始的 DOM

缺陷:
第一次生成了一個完整的 DOM 片段
第二次生成了一個完整的 DOM 片段
第二次的 DOM 替換了第一次的 DOM,非常耗性能

  • React 優(yōu)化后,React 中 DOM 的渲染流程:
    1. state 數(shù)據(jù)
    2. JSX 模板
    3. 數(shù)據(jù) + 模板 結(jié)合,生成真實的 DOM,來顯示
    4. state 發(fā)生改變
    5. 數(shù)據(jù) + 模板 結(jié)合,生成真實的 DOM,并不直接替換原始的 DOM
    6. 新的 DOM( DocumentFragment )和原來的 DOM 樹做對比,找差異( 耗性能 )
    7. 找出發(fā)生變化的節(jié)點
    8. 只能新的 DOM 中發(fā)生變化的節(jié)點替換掉老的 DOM 中的相應節(jié)點

缺陷:
性能提升并不明顯

  • 引入虛擬 DOM 后,React 中 DOM 的渲染流程:
    1. state 數(shù)據(jù)
    2. JSX 模板
    3. 數(shù)據(jù) + 模板,生成虛擬 DOM ( 虛擬 DOM 就是一個 JS 對象,用它來描述真實的 DOM )( 損耗了性能 )
      [ 'div', { id: "root" }, [ 'span', { }, 'hello world' ] ]
    4. 用虛擬 DOM 的結(jié)構(gòu)生成真實的 DOM,來顯示
      <div id="root"><span>hello world</span></div>
    5. state 發(fā)生變化
    6. 數(shù)據(jù) + 模板 結(jié)合,生成新的虛擬 DOM( 極大提升了性能 )
      [ 'div', { id: "root" }, [ 'span', { }, 'bye bye' ] ]
    7. 比較新的虛擬 DOM 和原始虛擬 DOM 的區(qū)別,找到區(qū)別是 span 中的內(nèi)容( 極大提升了性能 )
    8. 直接操作 DOM,改變 span 中的內(nèi)容
      總結(jié):JSX -> React.createElement -> 虛擬 DOM (JS對象)-> 真實的DOM

優(yōu)點:
性能提升
它使得跨端應用得以實現(xiàn)。React Native

DOM Diff 算法:

  • 首先當改變 state 數(shù)據(jù)時,由于 setState 是一個異步函數(shù)( 官方文檔原話:不保證它是同步的 ),這里關(guān)于異步的問題我們暫時這么理解,如果深究可以自行去其他博客學習,當你一次調(diào)用三次 setState 時,React 并不會生成三次虛擬 DOM 而是將三次 setState 合并,然后生成一次虛擬 DOM 進行比對,接下來:


  • 當新的虛擬 DOM 與原來的虛擬 DOM 進行比對時,它會進行同層比較,即相同的節(jié)點層進行比較,如果不同則直接將原始虛擬 DOM 中該節(jié)點層及以下的節(jié)點全部刪除,重新生成新的虛擬 DOM 節(jié)點,而不會繼續(xù)向下比對。


  • 這里順便提一點,當你在 JSX 模板中遍歷 state 中某個數(shù)據(jù)時,為什么不加 key 值瀏覽器會報錯,這是因為你不再遍歷的每條數(shù)據(jù)加上 key 值,更改 state 中那條數(shù)據(jù)的值,生成虛擬 DOM 后,React 就不知道原始遍歷的數(shù)據(jù)和這次更新后遍歷的數(shù)據(jù)一一對應的關(guān)系,就會再次重新渲染,而加上 key 值,它則能迅速比對出有差異的部分進行部分的更新,但是為什么不建議用 index 作為 key 值呢?因為當你插入 / 刪除中間的數(shù)據(jù)時,從改變的那個數(shù)據(jù)開始,后續(xù)每個數(shù)據(jù)的 index 值就會變,從而就導致了每個數(shù)據(jù)的 key 值相應變化了,這樣依舊會引起大規(guī)模渲染,這就是其中的原因啦。


總結(jié):

  • 這篇文章真的干貨滿滿,上面的內(nèi)容都是博主精心總結(jié)的,建議各位讀者細讀,必有大收獲!
  • Vue 2.x 的虛擬 DOM 原理和 DOM Diff算法與 React 的基本大同小異,一篇懂,全都懂!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容