React——diff算法
來自: http://hao.jser.com/archive/9548/
這篇文章只是個人理解,有什么差異和謬誤還望大家指出:
不知道是從什么時候開始,寫JavaScript的時候,腦袋里面就會一直回響著一句話,盡量避免DOM操作,原因是DOM操作比較消耗性能,特別是在復雜的DOM結構中進行DOM操作。而當我們使用各種各樣前端模板引擎的時候,更是無法避免不停地操作DOM。
虛擬DOM
React出于性能的考慮,為了避免頻繁操作DOM,采用了虛擬DOM結構(virtual DOM):
每當虛擬DOM樹發生變化樹發生變化時,React會將當前DOM樹和之前的虛擬DOM樹進行diff算法對比,得到虛擬DOM結構的區別,然后僅僅渲染差異部分。
var MyComponent = React.createClass({ render: function() { if (this.props.first) { return <div className="first"><span>A Span</span></div>; } else { return <div className="second"><p>A Paragraph</p></div>; } } });
這里MyComponent執行render方法的結果并不是一個真實的DOM節點,而是一個輕量級的JavaScript對象,即虛擬DOM。
-
如果我們先插入組件:<MyComponent first={true} />;React會創建真實DOM節點:<div className=”first”><span>A Span</span></div>
-
將之前的組件替換為:<MyComponent first={false} />;React會替換之前節點的屬性:className=”first”為className=”second”,同時替換子節點<span>A Span</span>為<p>A Paragraph</p>
-
最后移除組件;React會移除節點<div className=”second”><p>A Paragraph</p></div>
如果將所有虛擬節點進行改變前后的diff算法比較,這同樣是一件消耗性能的過程(兩個樹的比較復雜度為O(n^3)),React采用了以下優化方案,將性能優化到O(n)。
分層比較
React將虛擬樹進行分層比較,這是因為節點操作很少存在跨層級的(比如將子節點移動到父節點外,變成父節點的兄弟節點),大多操作多是在兄弟節點之間的。如下圖中,比較只會在相同顏色的兄弟節點之間進行,一旦節點被刪除,則所有子節點都會被刪除,不會進行比較。
節點列表
假設有這樣的情況,一個組件初始化時渲染了5個子節點,第二個時鐘周期時向這5個子節點中間插入一個新的組件。這時,如果直接對比前后兩個子節點樹,新插入節點之后的所有子節點都會被重新渲染(C被替換為F,D被替換為C,E被替換為D),因為他們和之前的節點樹不匹配。如同下圖的情況:
React在組件插入的過程中,只會將同級子節點按前后順序排列起來,但是,如果給予每個子節點一個唯一的key值,這樣每個子節點都能找到與之對應的節點進行比較。
組件層面
節點diff算法只會在相同的組件類型上進行,意味著如果一個<Header>組件被替換成了<ExampleBlock>,這時前后節點樹不會進行對比。React做出這樣的取舍是因為耗費大量計算去匹配兩個幾乎不會相似的組件是一種浪費。而事實上,大多數用戶會用大量的div去構建一個節點樹,React在不會匹配不同class的組件。
參考文章: http://www.infoq.com/cn/articles/react-dom-diff?from=timeline&isappinstalled=0