摘要:,文本節點的比較,需要修改,則會調用。兩個節點都有子節點,而且它們不一樣,這樣我們會調用函數比較子節點,這是的核心。,新節點沒有子節點,老節點有子節點,直接刪除老節點。參考文章解析的算法
判斷對應節點是否有必要進行比較(sameVnode)
function sameVnode(oldVnode, vnode){ return vnode.key === oldVnode.key && vnode.sel === oldVnode.sel }
如果值得比較會執行patchVnode(oldVnode, vnode)
如果不值得比較,新節點直接把老節點整個替換了
打補丁(patchVnode)patchVnode (oldVnode, vnode) { const el = vnode.el = oldVnode.el let i, oldCh = oldVnode.children, ch = vnode.children if (oldVnode === vnode) return if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) { api.setTextContent(el, vnode.text) }else { updateEle(el, vnode, oldVnode) if (oldCh && ch && oldCh !== ch) { updateChildren(el, oldCh, ch) }else if (ch){ createEle(vnode) //create el"s children dom }else if (oldCh){ api.removeChildren(el) } } }
節點的比較有5種情況
if (oldVnode === vnode),他們的引用一致,可以認為沒有變化。
if(oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text),文本節點的比較,需要修改,則會調用Node.textContent = vnode.text。
if( oldCh && ch && oldCh !== ch ), 兩個節點都有子節點,而且它們不一樣,這樣我們會調用updateChildren函數比較子節點,這是diff的核心。
else if (ch),只有新的節點有子節點,調用createEle(vnode),vnode.el已經引用了老的dom節點,createEle函數會在老dom節點上添加子節點。
else if (oldCh),新節點沒有子節點,老節點有子節點,直接刪除老節點。
更新子節點(updateChildren)updateChildren (parentElm, oldCh, newCh) { let oldStartIdx = 0, newStartIdx = 0 let oldEndIdx = oldCh.length - 1 let oldStartVnode = oldCh[0] let oldEndVnode = oldCh[oldEndIdx] let newEndIdx = newCh.length - 1 let newStartVnode = newCh[0] let newEndVnode = newCh[newEndIdx] let oldKeyToIdx let idxInOld let elmToMove let before while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { if (oldStartVnode == null) { //對于vnode.key的比較,會把oldVnode = null oldStartVnode = oldCh[++oldStartIdx] }else if (oldEndVnode == null) { oldEndVnode = oldCh[--oldEndIdx] }else if (newStartVnode == null) { newStartVnode = newCh[++newStartIdx] }else if (newEndVnode == null) { newEndVnode = newCh[--newEndIdx] }else if (sameVnode(oldStartVnode, newStartVnode)) { patchVnode(oldStartVnode, newStartVnode) oldStartVnode = oldCh[++oldStartIdx] newStartVnode = newCh[++newStartIdx] }else if (sameVnode(oldEndVnode, newEndVnode)) { patchVnode(oldEndVnode, newEndVnode) oldEndVnode = oldCh[--oldEndIdx] newEndVnode = newCh[--newEndIdx] }else if (sameVnode(oldStartVnode, newEndVnode)) { patchVnode(oldStartVnode, newEndVnode) api.insertBefore(parentElm, oldStartVnode.el, api.nextSibling(oldEndVnode.el)) oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] }else if (sameVnode(oldEndVnode, newStartVnode)) { patchVnode(oldEndVnode, newStartVnode) api.insertBefore(parentElm, oldEndVnode.el, oldStartVnode.el) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] }else { // 使用key時的比較 if (oldKeyToIdx === undefined) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) // 有key生成index表 } idxInOld = oldKeyToIdx[newStartVnode.key] if (!idxInOld) { api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el) newStartVnode = newCh[++newStartIdx] } else { elmToMove = oldCh[idxInOld] if (elmToMove.sel !== newStartVnode.sel) { api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el) }else { patchVnode(elmToMove, newStartVnode) oldCh[idxInOld] = null api.insertBefore(parentElm, elmToMove.el, oldStartVnode.el) } newStartVnode = newCh[++newStartIdx] } } } if (oldStartIdx > oldEndIdx) { before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].el addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx) }else if (newStartIdx > newEndIdx) { removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) } }
主要的思路大概就是,定義變量,分別記錄當前比較的新、舊節點中的首尾索引與節點(oldStartVnode、oldEndVnode、newStartVnode、newEndVnode)(后續稱為比較區間)
(圖片來自:https://github.com/aooy/blog/... )
通過對 oldStartVnode、oldEndVnode、newStartVnode、newEndVnode 做,兩兩的 sameVnode 比較
比較判斷有4種,按順序依次比較
oldStartVnode —— newStartVnode (頭對頭)
oldEndVnode —— newEndVnode (尾對尾)
oldStartVnode —— newEndVnode (頭對尾)
oldEndVnode —— newStartVnode (尾對頭)
如果其中一種比較成立了,那么oldVode中的相應節點會 以當前比較區間為基準 移到newVnode相應的位置上,
然后比較區間會根據當前的比較條件類型,以頭或尾為縮小比較區間的方向,縮小區間
例如: 當oldStartVnode,newEndVnode值得比較時, 將oldStartVnode.el移動到oldEndVnode.el后邊
當4種比較都不成立時,會使用key去比較,并在最終都使newVode的比較區間,頭部 減1
當oldVnode的key列表中能匹配到對應的key時,判斷比較節點的選擇器屬性是否一樣
不一樣則直接在當前比較區間的頭部,新創建一個newVnode的Dom插入
比較節點中的oldVnode無需處理,因為后面的比較中不會有成立的比較條件,最終會直接刪除節點
如果一樣則將比較節點中的oldVnode移動到當前比較區間的頭部(所以為節點設置key可以更高效的利用dom),并將比較區間中oldVnode原本的索引位置賦值為Null
如果最終key列表也沒能匹配到的話,也是直接在當前比較區間的頭部,新創建一個newVnode的Dom插入。
最后在結束時會存在2種情況
oldStartIdx > oldEndIdx
oldVnode先遍歷完,證明newVode有新增的節點,或者一致,這種情況會將newVode剩余的節點插入到oldVnode比較區間的末尾
newStartIdx > newEndIdx
這時是newVode先遍歷完,證明newVode里刪除了某些節點,此時oldVnode的比較區間節點是已經不存在的,會將他們刪除。
參考文章:解析vue2.0的diff算法
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103068.html
摘要:所以只針對同層級節點做比較,將復雜度的問題轉換成復雜度的問題。 React系列 React系列 --- 簡單模擬語法(一)React系列 --- Jsx, 合成事件與Refs(二)React系列 --- virtualdom diff算法實現分析(三)React系列 --- 從Mixin到HOC再到HOOKS(四)React系列 --- createElement, ReactElem...
摘要:以我自己的理解,函數式編程就是以函數為中心,將大段過程拆成一個個函數,組合嵌套使用。越來越多的跡象表明,函數式編程已經不再是學術界的最愛,開始大踏步地在業界投入實用。也許繼面向對象編程之后,函數式編程會成為下一個編程的主流范式。 使用React也滿一年了,從剛剛會使用到逐漸探究其底層實現,以便學習幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識點,算是React看書...
摘要:毫無疑問的是算法的復雜度與效率是決定能夠帶來性能提升效果的關鍵因素。速度略有損失,但可讀性大大提高。因此目前的主流算法趨向一致,在主要思路上,與的方式基本相同。在里面實現了的算法與支持。是唯一添加的方法所以只發生在中。 VirtualDOM是react在組件化開發場景下,針對DOM重排重繪性能瓶頸作出的重要優化方案,而他最具價值的核心功能是如何識別并保存新舊節點數據結構之間差異的方法,...
摘要:如果以上情況均不符合,則通過會得到一個,里面存放了一個為舊的,為對應序列的哈希表。從這個哈希表中可以找到是否有與一致的舊的節點,如果同時滿足,的同時會將這個真實移動到對應的真實的前面。 寫在前面 因為對Vue.js很感興趣,而且平時工作的技術棧也是Vue.js,這幾個月花了些時間研究學習了一下Vue.js源碼,并做了總結與輸出。文章的原地址:https://github.com/ans...
摘要:圖在中應用三數據渲染過程數據綁定實現邏輯本節正式分析從到數據渲染到頁面的過程,在中定義了一個的構造函數。一、概述 vue已是目前國內前端web端三分天下之一,也是工作中主要技術棧之一。在日常使用中知其然也好奇著所以然,因此嘗試閱讀vue源碼并進行總結。本文旨在梳理初始化頁面時data中的數據是如何渲染到頁面上的。本文將帶著這個疑問一點點追究vue的思路。總體來說vue模版渲染大致流程如圖1所...
閱讀 3062·2021-10-12 10:12
閱讀 1569·2021-09-09 11:39
閱讀 1845·2019-08-30 15:44
閱讀 2339·2019-08-29 15:23
閱讀 2898·2019-08-29 15:18
閱讀 2960·2019-08-29 13:02
閱讀 2688·2019-08-26 18:36
閱讀 733·2019-08-26 12:08