1、diff比較算法
圖示:
diff比較只會在同層級進行, 不會跨層級比較。
所以diff是:廣度優先算法。
時間復雜度:O(n)
代碼示例:
<!-- 之前 -->
<div> <!-- 層級1 -->
<p> <!-- 層級2 -->
<b> aoy </b> <!-- 層級3 -->
<span>diff</Span>
</P>
</div>
<!-- 之后 -->
<div> <!-- 層級1 -->
<p> <!-- 層級2 -->
<b> aoy </b> <!-- 層級3 -->
</p>
<span>diff</Span>
</div>
我們可能期望將<span>
直接移動到<p>
的后邊,這是最優的操作。
但是實際的diff操作是:
(1)移除<p>
里的<span>
(2)創建一個新的<span>
插到<p>
的后邊。
因為新加的<span>
在層級2,舊的在層級3,屬於不同層級的比較。
一般的diff算法中都采用的是深度優先遍歷。對新舊兩棵樹進行一次深度優先的遍歷,這樣每個節點都會有一個唯一的標記。在遍歷的時候,每遍歷到一個節點就把該節點和新的樹的同一個位置的節點進行對比,如果有差異的話就記錄到一個對象里面。
例如,上面的div和新的div有差異,當前的標記是0,那么:patches[0] = [{difference}, {difference}, …]。同理p是patches[1],ul是patches[3],以此類推。這樣當遍歷完整棵樹的時候,就可以獲得一個完整的差異對象。
vue源碼中會有一個sameVnode方法:
function sameVnode (a, b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) ) }
表示2個Vnode是否是同一個節點:
(1)當是一樣的節點,直接復用(若設置key的話)。
(2)當不是一樣的節點的話,新節點直接替換老節點。
2、比較原則
圖示:
圖師說明:
粉紅色的部分為oldNode,黃色的表示newNode。
s和e指針指向它們的頭child和尾child Node的指針。
現在分別對oldS、oldE、S、E
兩兩做sameVnode
比較,有四種比較方式。
即:
oldS == S?
oldS == E?
oldE == S?
oldE == E?
diff算法:
- 如果是oldS和E匹配上了,那么真實dom中的第一個節點會移到最后
- 如果是oldE和S匹配上了,那么真實dom中的最后一個節點會移到最前,匹配上的兩個指針向中間移動
- 如果四種匹配沒有一對是成功的,那么遍歷
oldChild
,S
挨個和他們匹配,匹配成功就在真實dom中將成功的節點移到最前面,如果依舊沒有成功的,那么將S對應的節點
插入到dom中對應的oldS
位置,oldS
和S
指針向中間移動。
3、key
不設key,newCh和oldCh只會進行頭尾兩端的相互比較。
設key后,除了頭尾兩端的比較外,還會從用key生成的對象oldKeyToIdx
中查找匹配的節點,所以為節點設置key可以更高效的利用dom。
(1)下圖是沒有設置key的diff算法
(2)下圖是有設置key的diff算法
參考文章:
https://www.cnblogs.com/wind-lanyan/p/9061684.html
https://segmentfault.com/a/1190000008782928?utm_source=tag-newest