Vue 3 使用 index 當 key 的問題
簡介
在 Vue 3 中,當我們使用 v-for 來渲染清單時,Vue 會根據 :key 來追蹤每個元素,這樣:
- 當陣列變動時,Vue 可以精準更新 DOM,而不是重新渲染整個清單
- 避免 UI 錯亂,例如表單輸入框內容消失
使用 index 當 key 的問題
雖然使用 index 當 key 很方便,但當陣列變動時,Vue 會重新渲染整個清單,這樣會導致效能問題。
錯誤範例
App.vue
<script setup>
import { ref } from "vue";
const items = ref([{ text: "項目 1" }, { text: "項目 2" }, { text: "項目 3" }]);
const addItem = () => {
items.value.push({ text: "新項目" });
};
const removeItem = (index) => {
items.value.splice(index, 1);
};
</script>
<template>
<div>
<h2>使用 index 作為 key(錯誤示範)</h2>
<ul>
<li v-for="(item, index) in items" :key="index">
<input v-model="item.text" />
<button @click="removeItem(index)">刪除</button>
</li>
</ul>
<button @click="addItem">新增項目</button>
</div>
</template>
因為我們的清單是使用 index
當 key
,所以當我們新增或刪除項目時,Vue 會重新渲染整個清單,這樣會導致效能問題,看畫面可能不太明顯,所以我們看 DOM。
當我們刪除 item1
的時候,會看到 DOM 的 ul li
都會閃一下,這是因為 Vue 重新渲染整個清單,導致 DOM 重新渲染。
正確範例
現在我們把 key
改成 item.id
,這樣當我們新增或刪除項目時,Vue 會精準更新 DOM,而不是重新渲染整個清單。
App.vue
<script setup>
import { ref } from "vue";
const items = ref([
{ id: crypto.randomUUID(), text: "item 1" },
{ id: crypto.randomUUID(), text: "item 2" },
{ id: crypto.randomUUID(), text: "item 3" },
]);
const addItem = () => {
items.value.push({ id: crypto.randomUUID(), text: "new item" });
};
const removeItem = (index) => {
items.value.splice(index, 1);
};
</script>
<template>
<div>
<h2>使用獨一無二的 id 作為 key</h2>
<ul>
<li v-for="(item, index) in items" :key="item.id">
<input v-model="item.text" :placeholder="item.text" />
<button @click="removeItem(index)">刪除</button>
</li>
</ul>
<button @click="addItem">新增項目</button>
</div>
</template>
什麼時候 index 是安全的 ?
在 靜態陣列(不會變動)
或 清單只是顯示用途(沒有 v-model 或互動)
時,可以安全使用 index,也就是沒有新增
、刪除
、修改
的時候。
<ul>
<li v-for="(item, index) in items" :key="index">{{ index + 1 }}. {{ item }}</li>
</ul>