亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

詳解key在Vue列表渲染時究竟起到了什么作用

2019-11-19 11:44:36
字體:
來源:轉載
供稿:網友

Vue2+采用diff算法來進行新舊vnode的對比從而更新DOM節點。而通常在我們使用v-for這個指令的時候,Vue會要求你給循環列表的每一項添加唯一的key,那么這個key在渲染列表時究竟起到了什么作用呢?

在解釋這一點之前,你最好已經了解Vue的diff算法的具體原理是什么。

Vue2更新真實DOM的操作主要是兩種:創建新DOM節點并移除舊DOM節點和更新已存在的DOM節點,這兩種方式里創建新DOM節點的開銷肯定是遠大于更新或移動已有的DOM節點,所以在diff中邏輯都是為了減少新的創建而更多的去復用已有DOM節點來完成DOM的更新。

在新舊vnode的diff過程中,key是判斷兩個節點是否為同一節點的首要條件:

// 參見Vue2源碼 core/vdom/patch.jsfunction 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)      )    )  )}

值得注意的是,如果新舊vnode的key值都未定義的話那么兩個key都為undefined,a.key === b.key 是成立的

接下來是在updateChildren方法中,這個方法會對新舊vnode進行diff,然后將比對出的結果用來更新真實的DOM

// 參見Vue2源碼 core/vdom/patch.jsfunction updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {  ...  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {    if (isUndef(oldStartVnode)) {      ...    } else if (isUndef(oldEndVnode)) {      ...    } else if (sameVnode(oldStartVnode, newStartVnode)) {      ...    } else if (sameVnode(oldEndVnode, newEndVnode)) {      ...    } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right      ...    } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left      ...    } else {      if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)      idxInOld = isDef(newStartVnode.key)        ? oldKeyToIdx[newStartVnode.key]        : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)      if (isUndef(idxInOld)) { // New element        createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)      } else {        vnodeToMove = oldCh[idxInOld]        if (sameVnode(vnodeToMove, newStartVnode)) {          patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)          oldCh[idxInOld] = undefined          canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)        } else {          // same key but different element. treat as new element          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)        }      }      newStartVnode = newCh[++newStartIdx]    }  }  ...}

設置key的可以在diff中更快速的找到對應節點,提高diff速度

在updateChildren方法的while循環中,如果頭尾交叉對比沒有結果,即oldStartVnode存在且oldEndVnode存在且新舊children首尾四個vnode互不相同的條件下,會根據newStartVnode的key去對比oldCh數組中的key,從而找到相應oldVnode

首先通過createKeyToOldIdx方法創建一個關于oldCh的map

if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)function createKeyToOldIdx (children, beginIdx, endIdx) {  let i, key  const map = {}  for (i = beginIdx; i <= endIdx; ++i) {    key = children[i].key    if (isDef(key)) map[key] = i  }  return map}

這個map中將所有定義了key的oldVnode在數組中的index值作為鍵值,它的key作為鍵名存儲起來,然后賦給oldKeyToIdx

idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)function findIdxInOld (node, oldCh, start, end) {  for (let i = start; i < end; i++) {    const c = oldCh[i]    if (isDef(c) && sameVnode(node, c)) return i  }}

如果newStartVnode的key存在的話,就去oldKeyToIdx中尋找相同key所對應的index值,這樣就能拿到跟newStartVnode的key相同的oldVnode在oldCh數組中的index,即得到了與newStartVnode對應的oldVnode。如果找不到的話,那么idxInOld就為undefined。

而如果newStartVnode并沒有設置key,則通過findIdxInOld方法遍歷oldCh來獲取與newStartVnode互為sameVnode的oldVnode,返回這個oldVnode在oldCh數組的index。(前面介紹過,Vue在更新真實DOM時傾向于真實DOM節點的復用,所以在這里還是會選擇去找對應的oldVnode,來更新已有的DOM節點)

這時候設置key的好處就顯而易見了,有key存在時我們可以通過map映射快速定位到對應的oldVnode然后進行patch,沒有key值時我們需要遍歷這個oldCh數組然后去一一進行比較,相比之下肯定是key存在時diff更高效。

接下來就是更新DOM的過程,如果oldCh[idxInOld]存在且與newStartVnode互為sameVnode存在則先更新再移動,否則創建新的element

if (isUndef(idxInOld)) { // New element  createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)} else {  vnodeToMove = oldCh[idxInOld]  if (sameVnode(vnodeToMove, newStartVnode)) {    patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)    oldCh[idxInOld] = undefined    canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)  } else {    // same key but different element. treat as new element    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)  }}

那么設置key值就一定能提高diff效率嗎?

答案是否定的

`<div v-for="i in arr">{{ i }}</div>`// 如果我們的數組是這樣的[1, 2, 3, 4, 5]// 它的渲染結果是這樣的`<div>1</div>` // key: undefined`<div>2</div>` // key: undefined`<div>3</div>` // key: undefined`<div>4</div>` // key: undefined`<div>5</div>` // key: undefined// 將它打亂[4, 1, 3, 5, 2]// 渲染結果是這樣的 期間只發生了DOM節點的文本內容的更新`<div>4</div>` // key: undefined`<div>1</div>` // key: undefined`<div>3</div>` // key: undefined`<div>5</div>` // key: undefined`<div>2</div>` // key: undefined// 如果我們給這個數組每一項都設置了唯一的key[{id: 'A', value: 1}, {id: 'B', value: 2}, {id: 'C', value: 3}, {id: 'D', value: 4}, {id: 'E', value: 5}]// 它的渲染結果應該是這樣的`<div>1</div>` // key: A`<div>2</div>` // key: B`<div>3</div>` // key: C`<div>4</div>` // key: D`<div>5</div>` // key: E// 將它打亂[{id: 'D', value: 4}, {id: 'A', value: 1}, {id: 'C', value: 3}, {id: 'E', value: 5}, {id: 'B', value: 2}]// 渲染結果是這樣的 期間只發生了DOM節點的移動`<div>4</div>` // key: D`<div>1</div>` // key: A`<div>3</div>` // key: C`<div>5</div>` // key: E`<div>2</div>` // key: B

我們給數組設置了key之后數組的diff效率真的變高了嗎?

并沒有,因為在簡單模板的數組渲染中,新舊節點的key都為undefined,根據sameVnode的判斷條件,這些新舊節點的key、tag等屬性全部相同,所以在sameVnode(oldStartVnode, newStartVnode)這一步的時候就已經判定為對應的節點(不再執行頭尾交叉對比),然后直接進行patchVnode,根本沒有走后面的那些else。每一次循環新舊節點都是相對應的,只需要更新其內的文本內容就可以完成DOM更新,這種原地復用的效率無疑是最高的。

而當我們設置了key之后,則會根據頭尾交叉對比結果去執行下面的if else,進行判斷之后還需要執行insertBefore等方法移動真實DOM的節點的位置或者進行DOM節點的添加和刪除,這樣的查找復用開銷肯定要比不帶key直接原地復用的開銷要高。

Vue文檔中對此也進行了說明:

當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它默認用“就地復用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單復用此處每個元素,并且確保它在特定索引下顯示已被渲染過的每個元素。

這個默認的模式是高效的,但是只適用于不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。

建議盡可能在使用 v-for 時提供 key,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴默認行為以獲取性能上的提升。

所以,簡單列表的渲染可以不使用key或者用數組的index作為key(效果等同于不帶key),這種模式下性能最高,但是并不能準確的更新列表項的狀態。一旦你需要保存列表項的狀態,那么就需要用使用唯一的key用來準確的定位每一個列表項以及復用其自身的狀態,而大部分情況下列表組件都有自己的狀態。

總結

key在列表渲染中的作用是:在復雜的列表渲染中快速準確的找到與newVnode相對應的oldVnode,提升diff效率

以上所述是小編給大家介紹的key在Vue列表渲染時究竟起到了什么作用詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品丝袜久久久久久不卡| 国产亚洲综合久久| 亚洲日本aⅴ片在线观看香蕉| 欧美又大又硬又粗bbbbb| 国产综合在线视频| 亚洲一区二区三区在线免费观看| 91精品国产高清自在线| 91在线网站视频| 欧美亚洲激情在线| 亚洲精品97久久| 欧美极品少妇xxxxⅹ裸体艺术| 久久久久久久久91| 亚洲精品中文字幕有码专区| 日韩中文有码在线视频| 亚洲第一视频网站| 欧美极品在线播放| 久久青草福利网站| 欧美xxxx18性欧美| 国产日韩欧美一二三区| 国产精品吊钟奶在线| 黄网动漫久久久| 久久久精品一区二区| 国产精品69精品一区二区三区| 久久福利网址导航| 欧美性感美女h网站在线观看免费| 亚洲人精品午夜在线观看| 97在线观看免费| 亚洲国产中文字幕久久网| 中文字幕视频一区二区在线有码| 国产精品极品尤物在线观看| 理论片在线不卡免费观看| xx视频.9999.com| 韩国三级日本三级少妇99| 91在线观看免费网站| 久久精品成人欧美大片| 国产亚洲人成a一在线v站| 亚洲视频国产视频| 亚洲成色999久久网站| 国产一区二区黄| 中文字幕一区二区精品| 欧美理论电影网| 黑人狂躁日本妞一区二区三区| 成人精品视频久久久久| 亚洲天堂男人的天堂| 国产一区二区av| 中国人与牲禽动交精品| 国产视频久久久久久久| 国内精品久久久久| 日韩在线观看免费全| 亚洲人成电影网站色| 亚洲人成自拍网站| 亚洲精品美女久久久| 永久免费看mv网站入口亚洲| 亚洲国模精品一区| 欧美在线免费看| 揄拍成人国产精品视频| 亚洲国产精品高清久久久| 国产精品视频在线观看| 欧美俄罗斯乱妇| 亚洲日本成人女熟在线观看| 国产精品久久久久久久久久新婚| 欧美激情一区二区三区在线视频观看| 国产日韩换脸av一区在线观看| 中文字幕亚洲欧美日韩2019| 国内精品视频久久| 成人黄色网免费| 欧美xxxx做受欧美.88| 国产成人综合一区二区三区| 91在线中文字幕| 欧美激情啊啊啊| 国产精品扒开腿爽爽爽视频| 久久久在线观看| 国产一区二区美女视频| 亚洲欧美在线看| 久久久免费在线观看| 欧美精品免费在线观看| 欧美刺激性大交免费视频| 久久精品国产69国产精品亚洲| 成人精品视频在线| 国产高清在线不卡| 日韩在线欧美在线| 国产丝袜一区二区三区| 精品亚洲男同gayvideo网站| 中文字幕亚洲天堂| 一本色道久久综合狠狠躁篇的优点| 色综合久综合久久综合久鬼88| 国产精品国产三级国产专播精品人| 亚洲精品国产精品自产a区红杏吧| 欧美性xxxxxx| 国产精品电影久久久久电影网| 国产在线视频一区| 国外成人在线播放| 亚洲第一免费网站| 亚洲japanese制服美女| 色777狠狠综合秋免鲁丝| 岛国av午夜精品| 中文字幕亚洲一区| 66m—66摸成人免费视频| 91精品国产色综合久久不卡98| 国产亚洲视频在线观看| 国产亚洲在线播放| 不卡伊人av在线播放| 国产一区二区三区在线免费观看| 日韩免费中文字幕| 欧美色图在线视频| 国产精品永久免费| 亚洲美女免费精品视频在线观看| 国产精品福利观看| 性欧美亚洲xxxx乳在线观看| 久久久久久久国产精品| 日韩第一页在线| 日韩av免费看网站| 欧美综合国产精品久久丁香| 岛国av一区二区三区| 久久亚洲电影天堂| 亚洲欧美制服综合另类| 欧美黑人一级爽快片淫片高清| 亚洲最大av网站| 欧美大尺度激情区在线播放| 久久久久久69| 欧美激情精品久久久久久黑人| 久热爱精品视频线路一| 伦伦影院午夜日韩欧美限制| 国产精品十八以下禁看| 97人人做人人爱| 亚洲黄色av网站| 亚洲欧洲高清在线| 亚洲人午夜色婷婷| 亚洲精品美女在线观看| 亚洲美女视频网站| 国产日韩在线观看av| 97超级碰在线看视频免费在线看| 日日骚av一区| 日韩网站在线观看| 九九九热精品免费视频观看网站| 超薄丝袜一区二区| 国外视频精品毛片| 亚洲高清av在线| 啪一啪鲁一鲁2019在线视频| 日韩精品视频在线| 日韩欧美国产视频| 国产成人涩涩涩视频在线观看| 国产精品欧美一区二区三区奶水| 亚洲直播在线一区| 欧美另类交人妖| 久久综合九色九九| 亚洲精品国产精品自产a区红杏吧| 一本色道久久88综合日韩精品| 亚洲精品成人网| 亚洲第一免费播放区| 亚洲综合在线小说| 欧美一级高清免费| 久久精品视频在线| 日韩亚洲国产中文字幕| 亚洲免费高清视频| 久久久之久亚州精品露出| 午夜精品在线视频| 日韩电影大全免费观看2023年上| 亚洲网在线观看| 欧美电影在线免费观看网站| 亚洲欧美日韩中文视频| 久久久电影免费观看完整版| 国内精品国产三级国产在线专| 欧洲精品在线视频|