在 React 中其實是 render 函數中return 的內容會生成 DOM,return 中的內容由兩部分組成,一部分是 JSX ,另外一部分就是 state 中的數據,因此簡單來說,在 React 中 JSX 結合 state 就生成了 DOM。html
如今拋開虛擬 DOM 不談,若是讓咱們去實現 React 中當數據發生變化時如何操做 DOM 來實現頁面內容的變化,咱們會怎樣去實現?算法
第一種方案:數組
1)JSX + state 生成真實的 DOM,並顯示在頁面上瀏覽器
2)state 發生變化dom
3)此時 JSX + state 再次結合生成新的真實的 DOM異步
4)新的 DOM 直接替換掉原來的 DOM函數
這樣頁面會發生變化,可是生成真實的 DOM 和在頁面上再從新加載新的 DOM 都比較耗性能。性能
第二種方案:spa
1)JSX + state 生成真實的 DOM,並顯示在頁面上code
2)state 發生變化
3)此時 JSX + state 再次結合生成新的真實的 DOM
4)新的 DOM 和原始的 DOM 做對比,找出差別
5)利用找出的差別,替換掉頁面上原始 DOM 的相應部分
此時頁面也會發生變化,和方案一相比多了對比步驟可是隻須要替換掉原始DOM的一部分便可,綜合來講,方案二要優於方案一。
第三種方案
1)JSX + state 生成虛擬 DOM(虛擬 DOM 就是一個 JS 對象,用它來描述真實 DOM)
例以下面這段代碼:
<div id='abc'>item</div>
注意上面的 div
,span
標籤時 JSX 語法,並非真實的 DOM,這裏是先生成虛擬 DOM ,而後再下一步的時候才用虛擬 DOM 生成真實的 DOM,由 JSX 到真實的 DOM 中間有一個虛擬 DOM。
JSX -> 虛擬DOM(JS對象) -> 真實DOM
也就是說,JSX 須要先轉換爲 JS 對象,而後再轉換爲真實的 DOM。
生成的虛擬 DOM 爲
['div',{id: 'abc'}, 'item']
虛擬 DOM 的格式爲
['標籤名',標籤屬性對象,子標籤]
那麼 <div id='abc'>item</div>
是如何轉化爲 JS 對象的呢?
實際上在 React 中上面這樣寫就至關於下面這樣寫:
React.createElement('div', {id: 'abc'}, 'item');
那麼實際上就算是沒有 JSX 語法經過上面這樣寫也是能夠的,可是會很是不方便。
2)用虛擬 DOM 的結構生成真實的 DOM 顯示在頁面上。
3)JSX + state 生成新的虛擬 DOM
4)兩個虛擬 DOM 進行對比,找出差別
5)根據差別直接修改替換頁面上的 DOM
虛擬 DOM 是一個 JS 對象,生成一個虛擬 DOM 比生成一個真實的 DOM 結構要容易省時地多,並且兩個虛擬 DOM(JS 對象) 之間的對比也比較簡單,因此方案三最佳。
React 中使用的也是第三種方案的思想。
那麼虛擬DOM的優勢到底有哪些呢?
1)性能提高
這一點經過上面的比較就能夠看得出來
2)使得跨端應用得以實現,例如原生應用。
React Native 可以作原生應用虛擬 DOM 是很重要的一方面,原生應用中是沒有 DOM 這個概念的,DOM 是瀏覽器中存在的,可是有了虛擬 DOM(JS 對象) 以後,在原生應用中就能夠將虛擬 DOM(JS 對象) 轉換爲一些原生應用中可以支持的原生組件在原生應用中顯示。
使用虛擬 DOM 時很重要的一個步驟就是兩個虛擬 DOM 之間的比較,那麼怎樣去進行比較呢?
React 中採用 diff 算法,簡單來講主要有如下三個方面:
1)當短期內連續調用屢次 setState 時,React 只會進行一次虛擬 DOM 的比對。
咱們知道當 state 或者 props 發生變化時,頁面會發生變化,實際上 props 的變化也是由於父組件 state 的變化,因此當頁面發生變化時其實是調用 setState 致使數據發生變化變化時。當短期內連續調用屢次 setState 時,若是每次都進行一次虛擬 DOM 的比對,那麼性能會比較低,反之屢次調用 setState 只進行一次虛擬 DOM 的比對會提高性能。這也是爲何 setState 要設置成異步的緣由,由於若是同步的話當執行完一次 setState 時就會發生一次虛擬 DOM 的比對。(同步是順序當即執行,異步是當全部的同步程序執行完後再執行)
2)在比較虛擬 DOM 時採用逐層同層比較,當上一層出現差別時,那麼下面的各層就不須要再比較了,下面各層的 DOM 都將被新的 DOM 替換。
這樣作看起來,複用性不是很好,由於下面各層有可能會有許多相同的 DOM。可是這樣作會使得比較算法很是簡單,比較的速度很是快。
3)設置 key 值
假設如今有一個數組 [a, b, c]
遍歷每一項顯示在頁面上,如今數組發生變化將第一項 a 刪掉,若是沒有 key 值,數組 [b, c]
沒法和原數組進行比對,例如 b 到底和原數組的哪個進行比較呢?
可是如今假設有了 key 值,原數組中 a 的 key 值是 a,b 的 key 值是 b,c 的 key 值是 c。刪除 a 以後,經過 key 值,b 的 key 值 b 在原數組中找到 b,說明 b 沒有發生變化,c 同理也沒有發生變化,可是原數組中的 a 在新數組中並無找到,說明新數組中將 a 刪掉了,因此在操做頁面時將 a 刪掉便可。
這裏有一點須要注意的是,key 值必定要選不能變化的,利用數組的索引來作 key 值就不可取。仍是以上面爲例進行說明。原數組的 a 的 key 值是 0,b 的 key 值是 1,c 的 key 值是 2,刪掉 a 後,新數組的 b 的 key 值是 0,c 的 key 值是 1,通過比對原數組的 a 和新數組的 b key 值相同,虛擬 DOM 會認爲它們是相同的,沒有差別,可是實際上它們是不一樣的。