VUE學習|使用v-for和checkbox中遇到的問題

本文記錄了我在實現一個簡單的TodoList的過程當中遇到的問題即解決方法。因爲我目前水平較低,仍有未明白的地方,同時文中也可能出現紕漏或者錯誤指出,如果有人能夠看到此文,但願能夠解答個人疑惑或者指出不正之處,謝謝。html

需求

在一個簡單的TodoList應用中,使用v-for指令綁定待辦事項列表進行渲染。待辦事項列表中每一個對象有一個Boolean類型的屬性代表該事項是否已經完成,在頁面中經過v-model將該屬性與一個checkbox進行雙向綁定,實現經過勾選設置事項是否完成。數組

如今要實現的是將用戶勾選標明已完成的事項放到下面,也就是在點擊checkbox後調整數組的位置,實現全部的已完成事項都出如今未完成事項的下面。同時,若是從新將已完成的事項取消勾選,其又會上升。函數

本來的作法以下:性能

<ul>
    <li v-for="item in todoList">
        <input type="checkbox" v-model="item.isFinished" @change="moveEvent(item, $event)">
        <span :class="{finished: item.isFinished}">{{ item.name }}</span>
        <button @click="removeEvent(item)">刪除</button>
        <button @click="topEvent(item)">置頂</button>
    </li>
</ul>
複製代碼

moveEvent()函數以下,作法比較粗暴,先從數組中移除原來的對象,而後找到合適的位置再加進去。ui

moveEvent(item, event){
    console.log(event.target.checked)
    if(event.target.checked){
        this.todoList.splice(this.todoList.indexOf(item), 1);
        var index;
        for(index = 0; index < this.todoList.length; index++){
            if(this.todoList[index].isFinished == true){
                break;
            }
        }
        this.todoList.splice(index, 0, item);
    }else{
        this.todoList.splice(this.todoList.indexOf(item), 1);
        var index;
        for(index = 0; index < this.todoList.length; index++){
            if(this.todoList[index].isFinished == true){
                break;
            }
        }
        this.todoList.splice(index, 0, item);
    }
}
複製代碼

出現的問題

初始狀態this

當我點擊列表的第一個元素的checkbox時,出現以下圖的狀況:spa

發現,雖然第一個事項是被移到了下面,而且成功修改成已完成,可是隨後因爲修改了數組產生的新的第一個元素的checkbox也被勾選上了,可是沒有出現已完成的CSS效果,而且刷新後就能夠正常顯示,說明其實際上並無被修改成已完成。雙向綁定

一樣的,取消勾選會致使相反的狀況,一樣刷新後即會正常顯示。code

緣由分析

能夠判斷咱們的數據並無出錯,而且數組元素的移動也是正常的,可是咱們的checkbox明明經過v-model進行了雙向綁定,爲何會出現顯示錯誤的狀況呢?通過定位,最終將問題鎖定在v-for指令上。先來看看官網對於v-for指令的介紹:cdn

當 Vue 正在更新使用 v-for 渲染的元素列表時,它默認使用「就地更新」的策略。若是數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序,而是就地更新每一個元素,而且確保它們在每一個索引位置正確渲染。這個相似 Vue 1.x 的 track-by="$index"。

其實這也是一個老生常談的問題了,就是說當咱們改變了數據項順序,Vue並無移動DOM元素,而是從新渲染每一個DOM上的數據,這樣的目的應該是爲了減小DOM操做,提升效率。

那麼回看咱們的問題,大體判斷緣由應該就是出於此:假設咱們把當前索引爲0的元素標記爲已完成,該元素被移到數組下方,而且新位置上渲染結果正確。可是此時因爲從新排序而出如今索引0位置的新元素的checkbox卻因爲DOM重用的結果,沒有獲得更新,後面的解決方案也驗證了是用於這個緣由形成的。

可是我是進行了v-model綁定了的,爲什麼仍是會出現這種狀況我尚未徹底明白,在網上查了也沒有類似狀況,如有看到此處的人明白的,還請不吝賜教

解決方案

對應的,Vue官網給出瞭解決方法:

爲了給 Vue 一個提示,以便它能跟蹤每一個節點的身份,從而重用和從新排序現有元素,你須要爲每項提供一個惟一 key 屬性。

建議儘量在使用 v-for 時提供 key attribute,除非遍歷輸出的 DOM 內容很是簡單,或者是刻意依賴默認行爲以獲取性能上的提高。

那麼嘗試將v-for改成:

<li v-for="(item, index) in todoList" :key="index">
複製代碼

結果發現,仍是不行。我認爲緣由應該是這樣的寫法用數組的地址做爲元素的身份,那麼原先被標記爲0(即數組中索引爲0的元素)被成功勾選並下移,而新的數組頭元素此時也被標記爲0,二者身份相同,因此又會錯誤的被勾上!

正確寫法(錯誤寫法)以下,這裏我使用了元素的name屬性,即事件的名稱:

<li v-for="item in todoList" :key="item.name">
複製代碼

尷尬😅,原本覺得上面的寫法是正確的,可是在寫本文的時候寫着寫着忽然以爲不對,既然索引會出現錯誤,那麼具備相同name屬性的事件是否是也會出現錯誤?去試驗了一下,果不其然...但仍是記錄下來給本身提個醒。

那麼,通過這麼屢次試驗得出的結論就是須要使用惟一的key屬性,才能保證萬無一失,例如給每一個事件一個不會重複的id

相關文章
相關標籤/搜索