小紅書面試:v-for循環(huán),為什么不建議使用index作為key?
Hello,大家好,我是 Sunday。
最近有個(gè)同學(xué)在小紅書三面的時(shí)候遇到了一個(gè)面試題:“v-for循環(huán),為什么不建議使用index作為key?”
按說(shuō)這個(gè)問(wèn)題并不復(fù)雜,所以該同學(xué)直接回答:“使用 index 作為 key 可能會(huì)導(dǎo)致渲染性能問(wèn)題,特別是數(shù)據(jù)源使用了 unshift 將數(shù)據(jù)添加到頭部時(shí),會(huì)導(dǎo)致 index 被重新規(guī)劃,從而導(dǎo)致重新渲染?!?/p>
然后面試官繼續(xù)追問(wèn):“為什么 index 變化了就會(huì)導(dǎo)致 dom 重新渲染?它的渲染機(jī)制是什么?”
額... 吃了沒(méi)有看過(guò)源碼的虧,導(dǎo)致這個(gè)問(wèn)題并沒(méi)有回答出來(lái)(但愿該同學(xué)這次小紅書面試可以順利通過(guò),畢竟都到技術(shù)三面了)。
所以,咱們今天就針對(duì)這個(gè)面試題來(lái)說(shuō)一下。
v-for 循環(huán)的渲染時(shí)機(jī)
在 Vue 的渲染中,有兩個(gè)比較重要的渲染時(shí)機(jī):渲染、更新。
- 渲染:指的是 DOM 的首次渲染。首次的渲染性能的消耗是固定的。
- 更新:指的是 數(shù)據(jù)變化后影響到的視圖變化,所謂的性能問(wèn)題,主要就集中在這里。
我們以這段代碼為例,以下代碼表示的就是一段 更新 的邏輯:
<template>
<div>
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
<button @click="change">change</button>
</div>
</template>
<script setup>
const list = ref([1, 2, 3])
const change = () => {
list.value = [3, 2, 1]
}
</script>
如果我們把 [1, 2, 3] 作為一組 dom,把 [3, 2, 1] 作為另外一組 dom,那么 dom 由 [1, 2, 3] 變成 [3, 2, 1] 的過(guò)程就是 DOM 更新。
key 在渲染中的作用
同時(shí),我們需要注意:針對(duì)于 v-for 的 dom 更新,一定是:兩組 dom 對(duì)比發(fā)生的變化。
而在 vue 中,一旦兩組 dom 對(duì)比更新,那么就會(huì)觸發(fā) diff 算法 的邏輯,而在 diff 中有這樣一段 vue 的源代碼 isSameVNodeType:
圖片
這段代碼是用來(lái)判斷 兩個(gè) DOM 是否相等的。
它的判斷條件有兩個(gè):
- type:表示當(dāng)前 dom 的類型,比如:li、div、p 等不同標(biāo)簽名
- key:v-for 循環(huán)中綁定的 key 值
那么由此我們就可以知道:在 vue 中,通過(guò) type + key 兩個(gè)屬性來(lái)判斷 dom 是否相等。
如果條件滿足(isSameVNodeType 返回 true),那么就不會(huì)重新渲染 dom,從而可以 提升性能
index 為什么會(huì)影響性能?
根據(jù)上面的內(nèi)容,我們可以知道:在 type 不變的前提下,key 就決定了 dom 是否要重新渲染。
假如,我們使用 index 作為 key,同時(shí)使用 unshift 方法為數(shù)組添加了一個(gè)新的元素,那么所有的 index 都會(huì)發(fā)生變化,從而導(dǎo)致:isSameVNodeType 返回 false,即:所有的 dom 全部重新渲染 從而影響性能