1. 程式人生 > 其它 >diff演算法是如何比較的,保證讓你看的明明白白的!

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了
作者:明月人倚樓
出處:https://www.cnblogs.com/IwishIcould/

想問問題,打賞了卑微的博主,求求你備註一下的扣扣或者微信;這樣我好聯絡你;(っ•̀ω•́)っ✎⁾⁾!

如果覺得這篇文章對你有小小的幫助的話,記得在右下角點個“推薦”哦,或者關注博主,在此感謝!

萬水千山總是情,打賞5毛買辣條行不行,所以如果你心情還比較高興,也是可以掃碼打賞博主(っ•̀ω•́)っ✎⁾⁾!

想問問題,打賞了卑微的博主,求求你備註一下的扣扣或者微信;這樣我好聯絡你;(っ•̀ω•́)っ✎⁾⁾!

支付寶 微信 本文版權歸作者所有,歡迎轉載,未經作者同意須保留此段宣告,在文章頁面明顯位置給出原文連線
如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。