在虛擬dom中diff的實現。
分別從3個方面:
1. DIFF抽象概念
diff是廣泛的概念,如git diff,js對象 diff等。兩棵樹做diff,即虛擬DOM中的diff算法。
diff算法的必要性:渲染真實DOM的開銷是很大的,輕微的操作都可能導致頁面重新排版,非常耗性能。 相對於DOM對象,js對象處理起來更快,而且更簡單。 通過diff算法對比新舊vdom之間的差異,可以批量的、最小化的執行 dom操作,從而提高性能。
diff時間復雜度分析:
常規:O(n^3) 遍歷樹1; 遍歷樹2; 排序; 1000個節點,十億的量級。
優化:O(n) 只比較同一層級 ;tag不相同,直接刪掉重建; 通過key來標識區分相同節點。
1. 只比較同一層級。
Web UI 中 DOM 節點跨層級的移動操作特別少,可以忽略不計。
如圖左,只比較同一層級的,即相同顏色的層級。如右圖,左側P3在右側的P標簽下,不做移動,而是做新增操作和刪除操作。
2.tag不相同,直接刪掉重建。
擁有不同類型的兩個組件將會生成不同的樹形結構。
如上圖,D和G節點的子節點相同,但是tag不同,則整棵樹都重新刪除重建。
3.通過key來標識區分相同節點。
開發者可以通過 key prop 來暗示哪些子元素在不同的渲染下能保持穩定。
左圖沒有key值,所以老的節點全部刪除,新的節點再全部創建。右圖添加key值,所以只需要醬A移動到B,將C移動到D,就可以得到與新樹一樣的老樹。
對比
相同點: 深度優先、同層級比較。 tag不同默認不同。 都借助key。 都有批量新增和刪除等。
不同點: 架構不同,React基於fiber鏈表結構實現,不能雙向查找。Vue基於數組,可以很方便通過下標查找。 react的設計一開始就有,vue演進過程中才出現的。
diff算法總結(5w1h)
what: 是一種對新舊虛擬DOM樹進行比較,得出兩者差異,確定最小DOM更新操作的算法。
why: 減少渲染真實DOM的開銷,提高性能。
When: 頁面更新,重新渲染時用到。
where: Vue中當數據發生改變時,set方法會讓調用Dep.notify通知所有訂閱者Watcher,訂閱者就會調用patch給真實的DOM打補丁。React在下一次 state 或 props 更新時, render()方法會返回一棵不同的樹。在構建fiber時,標記effectTag為Placement、Update、Deletion等。在commitWork構建真實DOM時,按照effectTag規則生成DOM。
Who: Vue(patch、patchVnode、updateChildren)。React(reconcileChildFibers、reconcileChildrenArray、updateHostComponent等)。 how: 深度優先、同層比較。tag不同,生成樹形結構不同。使用key來標識穩定節點。