diff算法是如何比較的,保證讓你看的明明白白的!


更新dom節點,最小力度去跟新

index.html
<body>
    <h1>你好啊!</h1>
    <button id="btn">該變數據</button>
    <div id="container"></div>
</body>
<script src="xuni/bundle.js"></script>
</html>

index.js文件
import {
  init,
  classModule,
  propsModule,
  styleModule,
  eventListenersModule,
  h,
} from "snabbdom";

let myVnode1 = h('ul', {}, [
  h('li', {}, '姓名'),
  h('li', {}, '年齡'),
  h('li', {}, '愛好'),
])
// 使用init函數創建 patch函數 
const patch = init([classModule, propsModule, styleModule, eventListenersModule])
const container = document.getElementById('container')
// 讓虛擬節點上樹
patch(container, myVnode1)

// 改變數據
let myVnode2 = h('ul', {}, [
  h('li', {}, '姓名'),
  h('li', {}, '年齡'),
  h('li', {}, '愛好'),
  h('li', {}, '性別'),

])
let btn = document.getElementById('btn')
btn.onclick = function () {
  patch(myVnode1,myVnode2)
}

發現的現象

當我手動去更改頁面中的數據的時候。
在點擊按鈕。我們發現只追加了性別。
我更改的數據並沒有跟新。
說明diff是進行最小力度去跟新的

那我們把數據添加在最前面會發生什么呢?

let myVnode1 = h('ul', {}, [
  h('li', {}, '姓名'),
  h('li', {}, '年齡'),
  h('li', {}, '愛好'),
])
// 使用init函數創建 patch函數 
const patch = init([classModule, propsModule, styleModule, eventListenersModule])
const container = document.getElementById('container')
// 讓虛擬節點上樹
patch(container, myVnode1)

// 改變數據
let myVnode2 = h('ul', {}, [
  //在最前面添加,發現跟剛才的比一樣了?
  //他將我們更改的數據復原了?
  //這個時候又小伙伴會說,diff不是最小粒度去更新了
  h('li', {}, '性別'),
  h('li', {}, '姓名'),
  h('li', {}, '年齡'),
  h('li', {}, '愛好'),
])
let btn = document.getElementById('btn')
btn.onclick = function () {
  patch(myVnode1,myVnode2)
}

diff不是最小粒度跟新?

在最前面添加,發現跟剛才的比一樣了?
他將我們更改的數據復原了?
這個時候又小伙伴會說,diff不是最小粒度去更新了?
其實diff一直都是最小力度跟新,是你忘記增加key值了。
我們加上key值看看呢??

添加key值后

let myVnode1 = h('ul', {}, [
  h('li', {key:'001'}, '姓名'),
  h('li', {key:'002'}, '年齡'),
  h('li', {key:'003'}, '愛好'),
])
// 使用init函數創建 patch函數 
const patch = init([classModule, propsModule, styleModule, eventListenersModule])
const container = document.getElementById('container')
// 讓虛擬節點上樹
patch(container, myVnode1)

// 改變數據
let myVnode2 = h('ul', {}, [
  h('li', {key:'00x'}, '性別'),
  h('li', {key:'001'}, '姓名'),
  h('li', {key:'002'}, '年齡'),
  h('li', {key:'003'}, '愛好'),
])
let btn = document.getElementById('btn')
btn.onclick = function () {
  patch(myVnode1,myVnode2)
}

添加key值頂級節點類型改變的情況

當我們添加key值后,發現數據果然是最小力度去更新的,對吧!
如果將ul更改為div,還是最小力度跟新嗎?

let myVnode1 = h('ul', {}, [
  h('li', {key:'001'}, '姓名'),
  h('li', {key:'002'}, '年齡'),
  h('li', {key:'003'}, '愛好'),
])
// 使用init函數創建 patch函數 
const patch = init([classModule, propsModule, styleModule, eventListenersModule])
const container = document.getElementById('container')
// 讓虛擬節點上樹
patch(container, myVnode1)

// 改變數據
let myVnode2 = h('div', {}, [
  h('li', {key:'001'}, '姓名'),
  h('li', {key:'002'}, '年齡'),
  h('li', {key:'003'}, '愛好'),
])
let btn = document.getElementById('btn')
btn.onclick = function () {
  patch(myVnode1,myVnode2)
}

這個時候我們增加上key值了,按照之前的操作。
發現一個問題。數據全部恢復最初始值了。

在采取diff算法比較:新舊節點進行比較,
比較只會在同層級進行, 不會跨層級比較。

如果兩個節點都是一樣的,那么就深入檢查他們的子節點。
果兩個節點不一樣那就說明 Vnode 完全被改變了(ul和div節點不一樣),
就可以直接使用新節點替換老節點。【他們的子代不會進行比較了】 

雖然這兩個節點不一樣但是他們的子節點一樣怎么辦?
別忘了,diff可是逐層比較的,
如果[第一層不一樣那么就不會繼續深入比較第二層了。
(我在想這算是一個缺點嗎?相同子節點不能重復利用了??...)

這個時候你可能會說:這個diff算法也不會那么牛逼呢!
並不是最優的。
【雖然這兩個節點不一樣但是他們的子節點一樣怎么辦?】
在我們工作中:其實這指一種非常合理的機制。
我們幾乎並不會出現這樣的情況
<ul v-if="falg">
    <li v-for="item,index" in list>{{item }}</li>
</ul>

<ol v-if="falg">
    <li v-for="item,index" in list>{{item }}</li>
</ol>
這樣的代碼在我們工作中幾乎是不會出現的呢?

什么叫做不會跨層比較?
<div>
    <p>123123 </p>
</div>
與
<div>
   <h2> <p>123123 </p> </h2>
</div>

div與div比較
p與h2比較
當p與h2比較的時候,他們他們節點不一樣,直接使用替換。
此時並不會在使用diff了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM