vue/React列表中key的做用

首先是Vue的組件的渲染方式-就地複用原則,官方的解釋是這樣的:node


因爲v-for循環渲染出的就是上面input這樣的dom元素相同,只是內容有些不一樣,因此當Vue從新渲染列表時,並不會修改每一個dom,由於修改dom的重繪和迴流時很耗時的,因此它會用元素來匹配數據項,就地更改元素,而且保證他們每一個索引位置能正確渲染。算法

這樣利用元素就地複用減小dom操做,提升了渲染速度。可是這種方式只適用於不依賴子組件狀態或臨時 DOM 狀態的列表。typescript

如:api

這個todo list的主要功能是添加,刪除todo事項,以及事項的完成和撤銷完成,並使用 localStorage在客戶端存儲數據。
寫完以後,我發現一個問題,就是當我點擊列表第一項的 checkbox表示完成這項todo時,下一項的 checkbox被自動選中(此時原來的第一項已經移動到已完成列表,這一項變成第一項),我以爲很奇怪,測試了一番又發現,在已完成列表中取消完成也會有這種狀況,總之點擊以後 checkbox的選中狀況有點錯亂。

這樣一個功能,須要修改的節點位置沒有改變,可是內容更新了,這雖然提升了複用性能,可是在複雜的表單會致使狀態出現錯位,也不會產生過分效果。那麼這種狀況怎麼解決呢?bash

key:給列表中每一項添加一個惟一的用於區分別的列表項的一個key,因此對應的value就是惟一的。dom

做用函數

  1. 更準確
    由於帶key就不是就地複用了,在sameNode函數 a.key === b.key對比中能夠避免就地複用的狀況。因此會更加準確。性能

  2. 更快
    利用key的惟一性生成map對象來獲取對應節點,比遍歷方式更快測試

虛擬dom到真實dom的隱射並反映,dom節點的比較須要用到diff算法ui

什麼是diff算法?

咱們須要渲染真實dom的時候每每會把生成一個虛擬節點 virtual DOM,當virtual dom某個節點的數據改變後生成一個新的Vnode,而後將Vnode和oldVnode對比,固然有不一樣的地方教就直接修改在真實DOM上,而後是oldVnode=Vnode

  • 真實DOM
<div>
    <p>123</p>
</div>複製代碼
  • virtual DOM (虛擬DOM)
var Vnode={
    tag:'div',
    children:[{
        tag:'p',text:'123'
    }]
    
}複製代碼

diff的比較方式

在同層級進行,不會跨層級比較

oldDOM

<div>
    <p>123</p>
</div>複製代碼

newDOM

<div>
    <span>2222</span>
</div>複製代碼

clipboard.png

  • 先對比DIV,發現兩個DIV不對等
  • 查看DIV的子元素P、SPAN,發現不對等
  • 查看P、SPAN沒有子元素,則移除P,增長SPAN

clipboard.png

如今咱們來看看在進行替換

對比節點函數

function patch(oldVnode, vnode) {
  // 對比是否相等
  if (sameVnode(oldVnode, vnode)) {
    patchVnode(oldVnode, vnode)
  } else {
    const oEl = oldVnode.el // 當前oldVnode對應的真實元素節點
    let parentEle = api.parentNode(oEl) // 父元素

    createELe(vnode) // 爲vnode生成新的元素

    if (parentEle !== null) {
      api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 將新的元素添加到父元素中
      api.removeChild(parentEle, oldVnode.el) // 移除之前的元素
    }
  }

  return vnode
}複製代碼

判斷二者是否相同函數

function sameVnode(a, b) {
  return (
    a.key === b.key && // 對比key
    a.tag === b.tag && // 對比標籤名
    a.isComment === b.isComment && // 是否爲註釋節點
    isDef(a.data) === isDef(b.data) && // 是否認義了data,或者其餘屬性
    sameInputType(a, b) //判斷是當<Input>時 是否type相同
  )
}複製代碼

匹配規則

  • 將Vnode的子節點Vch和oldVnode的子節點oldCh提取出來
  • oldCh和vCh各有兩個頭尾的變量StartIdx和EndIdx,它們的2個變量相互比較,一共有4種比較方式。若是4種比較都沒匹配,若是設置了key,就會用key進行比較,在比較的過程當中,變量會往中間靠,一旦StartIdx>EndIdx代表oldCh和vCh至少有一個已經遍歷完了,就會結束比較。

圖解

clipboard.png

clipboard.png

  • 若是oldS和E匹配上了,那麼真實DOM中的第一個節點會移到最後
  • 若是oldE和S匹配上了,那麼真實DOM中的最後一個節點會移到最後面,匹配上的兩個指針向中間移動
  • 若是四種匹配都沒有一對成功成功的,那麼遍歷oldChild,S挨個和他們匹配,匹配成功就在真實dom中講成功的節點移到最前面,若是沒有成功,那麼犟S對應的節點插入dom中對應的olds位置,olds和s指正向中間移動
  • 若是4種比較都沒匹配,若是設置了key,就會用key進行比較
相關文章
相關標籤/搜索