Vue列表刪掉一行後輸入內容跑位:key不能隨便用索引

列表第二行的備註明明填好了,刪掉第一行後,備註卻出現在另一筆資料旁邊。先看每行的key:如果使用陣列索引,刪除或排序後,同一個位置就可能代表不同資料,元件裡的輸入草稿卻被留在原位置。

key不是用來消除警告的裝飾。它告訴Vue哪些節點或元件仍是同一個項目。列表有輸入框、展開狀態或子元件草稿時,這個身分尤其重要。

用一段備註重現跑位

準備首頁101、分類頁102、聯絡頁103三筆資料,每行各有一個子元件保存備註。左側使用index當key,右側使用item.id。兩邊都在分類頁填「分類頁待修改」,接著刪掉首頁。

在本文Vue 3案例中,index版的分類頁變成空白,備註跑到聯絡頁103;ID版的備註仍跟著分類頁102。兩邊刪除後的資料順序相同,差別是Vue用什麼身分重用子元件。

索引key讓分類頁備註出現在聯絡頁103
刪掉首頁後,索引版的草稿仍留在原位置。
穩定ID key讓分類頁102仍保有原本備註
同一筆ID仍是同一個元件,分類頁備註留在分類頁。

這是自建Vue頁面的實際桌面操作,不是後台資料已被改寫。案例只保留前端草稿,沒有提交到伺服器;看到輸入跑位,還不能推成資料庫的內容也移到另一筆。

索引會變,資料身分應保持穩定

原本索引0、1、2對應101、102、103。刪除101後,索引0、1改成102、103。若key仍是0、1,原key1的元件就可能被重用來顯示103,並保留它的備註狀態。

<!-- 有輸入草稿的列表,不要用位置代表資料身分 -->
<NoteRow v-for="(item, index) in items"
  :key="index" :item="item" />

<!-- 使用同一筆資料穩定且唯一的ID -->
<NoteRow v-for="item in items"
  :key="item.id" :item="item" />

同一個列表裡,key應唯一。既有資料通常用後端提供的ID;還沒儲存的新項目,可以在建立資料時產生自己的穩定ID,之後編輯、排序與刪除都保留它。不要每次渲染才重新產生亂數,否則每次更新都像換了一批新元件。

標題不一定適合當key。兩筆文章可能同名,使用者也可能修改名稱;資料本身沒換,只因標題變動就重建元件,輸入狀態可能被清掉。要找的是身分,不是現在看起來不重複的文字。

這個例子的草稿存在子元件

以下以已建立的Vue 3單檔元件專案為前提。先建立NoteRow.vue,把這一段放進該檔;每個元件各自保存note,並使用v-model更新輸入值。

<script setup>
import { ref } from 'vue';
defineProps({ item: Object });
const note = ref('');
</script>
<template>
  <label>
    {{ item.title }}(ID {{ item.id }})
    <input v-model="note" placeholder="輸入備註">
  </label>
</template>

父元件再建立三筆資料,匯入NoteRow。第一段放父元件的script setup,第二段放同一個檔案的template;不是貼進普通HTML就能執行的完整頁面。

import { ref } from 'vue';
import NoteRow from './NoteRow.vue';
const items = ref([
  { id: 101, title: '首頁' },
  { id: 102, title: '分類頁' },
  { id: 103, title: '聯絡頁' }
]);
<button type="button" @click="items.shift()">刪除第一行</button>
<NoteRow v-for="item in items" :key="item.id" :item="item" />

用同一批資料把key改成index,就能比較差異。shift只修改這個前端陣列;正式刪除仍需自己的確認、API與權限流程,不能把這個按鈕直接當成管理後台的刪除功能。

不是所有v-model都會出現同一種跑位

如果輸入值直接綁在item.note,而且資料物件本身被正確保留,輸入值可能隨資料更新,看起來沒有這個草稿跑位。本文使用的是子元件自己的ref,才能清楚呈現「資料換了、元件狀態仍保留」的差別。

因此排查時也要找到狀態存在哪裡:父層資料、子元件、表單函式庫,或未綁定的DOM輸入。key處理元件身分,不會替你修好把備註寫進錯誤資料物件的事件程式。

若每次修改輸入都按index寫回資料,排序後仍可能寫錯目標。事件中優先傳入穩定ID或該筆資料,讓維護者能追到真正更新的是哪一筆;不要只修模板key,就假定所有資料操作都正確。

篩選消失後,key不會替你永久保存草稿

穩定key能幫助Vue辨識仍在列表中的項目,但項目被移除、元件被卸載後,子元件自己的草稿仍可能消失。如果需求是切換篩選再回來,備註也要保留,應把草稿放在更長期的資料層,依ID保存。

重新取得後端資料時,也核對ID是否真的穩定。若每次查詢都產生新的臨時ID,Vue會把它當成新項目。分頁中的第1行不是上一頁第1行,跨頁位置也不能直接代表資料身分。

多個元素一起代表一筆資料時,可以在template v-for上放key;不要把key只放在其中一個內層節點,又以為整組的身分已設定。元件列表與普通元素列表的結構不同,依實際渲染範圍核對。

ID與資料操作也要對得上

同一份資料若有數字102與字串「102」混用,先把來源與型別規則整理清楚。不要在畫面上看起來都叫102,就認為所有比對與儲存邏輯都相同。檢查實際陣列中有沒有缺ID或重複ID,尤其是合併兩個來源、載入下一頁之後。

刪除按鈕可以把item.id傳給處理函式,再以這個ID找到目標。畫面已篩選時,可見列表的索引不一定等於完整資料陣列的索引;拿畫面第2行直接刪原資料第2筆,即使key正確,也可能刪錯內容。

另外,穩定key會保留元件,不會自動把本地草稿改成後端新值。如果元件只在建立時把props複製進ref,稍後同一筆資料更新,草稿仍可能維持舊內容。先決定要保留未儲存輸入,還是讓外部資料覆蓋;這是資料同步規則,不能靠每次改key強迫重建來掩蓋。

刪除之外,再試排序與插入

先在第二筆填一段容易辨識的備註,再刪第一筆、把順序反轉、在前面插入新資料。每次確認標題、ID與備註仍對應,而不是只看列表沒有報錯。若資料有展開面板,也把展開狀態一起比對。

修改只限目標列表,保留原模板與資料操作。輸入值、焦點和提交資料各自核對;畫面留在正確一行,不表示表單已成功儲存。把key與寫入目標一起追清楚,才知道備註是否真的屬於同一筆資料。

參考資料

原文鏈接:https://wntheme.com/vue-list-key-input-state/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?