導讀:
- 當用原生 JS / jQuery 操作 DOM 時藏杖,瀏覽器會從構(gòu)建 DOM 樹開始從頭到尾渲染一遍 DOM 節(jié)點,而大量的 DOM 操作會頻繁的更新 DOM( 即再次從頭到尾渲染 ),這無疑是非常消耗瀏覽器性能的,因此由 FaceBook 團隊提出的虛擬 DOM 橫空出世,并運用在了 React 之上( 目前 Vue 2.x 也引入了這個概念 )低缩,接下來我們就分析一下虛擬 DOM 的原理。
虛擬 DOM 原理:
-
無虛擬 DOM 時曹货,React 中 DOM 的渲染流程:
- state 數(shù)據(jù)
- JSX 模板
- 數(shù)據(jù) + 模板 結(jié)合咆繁,生成真實的 DOM,來顯示
- state 發(fā)生改變
- 數(shù)據(jù) + 模板 結(jié)合顶籽,生成真實的 DOM玩般,替換原始的 DOM
缺陷:
第一次生成了一個完整的 DOM 片段
第二次生成了一個完整的 DOM 片段
第二次的 DOM 替換了第一次的 DOM,非常耗性能
-
React 優(yōu)化后礼饱,React 中 DOM 的渲染流程:
- state 數(shù)據(jù)
- JSX 模板
- 數(shù)據(jù) + 模板 結(jié)合坏为,生成真實的 DOM,來顯示
- state 發(fā)生改變
- 數(shù)據(jù) + 模板 結(jié)合镊绪,生成真實的 DOM匀伏,并不直接替換原始的 DOM
- 新的 DOM( DocumentFragment )和原來的 DOM 樹做對比,找差異( 耗性能 )
- 找出發(fā)生變化的節(jié)點
- 只能新的 DOM 中發(fā)生變化的節(jié)點替換掉老的 DOM 中的相應節(jié)點
缺陷:
性能提升并不明顯
-
引入虛擬 DOM 后蝴韭,React 中 DOM 的渲染流程:
- state 數(shù)據(jù)
- JSX 模板
- 數(shù)據(jù) + 模板够颠,生成虛擬 DOM ( 虛擬 DOM 就是一個 JS 對象,用它來描述真實的 DOM )( 損耗了性能 )
[ 'div', { id: "root" }, [ 'span', { }, 'hello world' ] ] - 用虛擬 DOM 的結(jié)構(gòu)生成真實的 DOM榄鉴,來顯示
<div id="root"><span>hello world</span></div> - state 發(fā)生變化
- 數(shù)據(jù) + 模板 結(jié)合履磨,生成新的虛擬 DOM( 極大提升了性能 )
[ 'div', { id: "root" }, [ 'span', { }, 'bye bye' ] ] - 比較新的虛擬 DOM 和原始虛擬 DOM 的區(qū)別,找到區(qū)別是 span 中的內(nèi)容( 極大提升了性能 )
- 直接操作 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 的基本大同小異妥畏,一篇懂邦邦,全都懂!