Vue項目中v-for數組刪除第n項元素產生渲染錯誤問題及解決方法

項目背景

最近使用Vue(版本2.9)開發一個項目時,要生成表單列表,因此使用了v-for來作循環,循環裏的元素(item)是一個子組件。同時每一個元素都有刪除按鈕,點擊後刪除當前元素。
初始代碼以下:
父組件:vue

<template>
    <div class="content-body">
        <div>任務</div>
          <div>
            <ul>
              <li v-for="(item,index) in selectionConditionList" :key="index" style="margin:10px 0">
                <v-selection-condition-list></v-selection-condition-list> <button @click="deleteSelectionCondition(index)">刪除</button>
              </li>
            </ul>
          </div>
          <div>
             <button @click="addNewSelectionTask">新建任務</button>
          </div>
    </div>
</template>

<script>
/* eslint-disable */
import vSelectionConditionList from './SelectionConditionList'
  export default
  {
    data()
    {
      return {
        selectionConditionList:[],
      }
    },
    methods: {
      // 添加新的用戶篩選條件
      addNewSelectionTask(){
        this.selectionConditionList.push({});
      },
      // 刪除用戶篩選條件
      deleteSelectionCondition(index){
        console.log("delete.."+index);
        this.selectionConditionList.splice(index,1);
      }
    },
    components:{
      vSelectionConditionList
    }
  }
</script>
<style>
  .div_center {
    text-align: center;;
    width:100%;
    margin:0 auto;
  }
</style>

子組件:segmentfault

<template>
  <input type="text" :value="inputName">
</template>

<script>
/* eslint-disable */
  export default
  {
    data()
    {
      return {
        inputName: Math.random()
      }
    },
    methods: {
    }
  }
</script>

出現的問題

運行代碼後,點擊新建任務,出現的結果以下圖:
圖片描述數組

點擊第一行的刪除按鈕,預期固然是刪掉第一行。然而出現的結果倒是最後一行被刪掉了,而其餘元素未變。刪除中間某元素也是最後一行被刪掉。這時經過console控制檯的打印輸出能夠看到,刪除的index索引是正確的。本人是vue新手,遇到此問題有些懵,查詢官方文檔及百度相關問題無果後,在segmentfault問答區提問,當時問題連接。然鵝提問一天之後,收到的回答仍是沒有徹底解決問題,因而繼續尋求解決方案。dom

解決方法

通過再次苦苦查詢相關問題的問答帖及文章,終於發現問題是出在v-for的:key上。關於v-for中的:key介紹參見此頁面:Vue2.0 v-for 中 :key 到底有什麼用?,內容一大堆balabala,總之是因爲虛擬DOM的緣由引發的,個人理解就是:表單列表的生成是經過綁定的selectionConditionList數組來生成的,當selectionConditionList刪除掉一項時,表單列表的dom對象天然也會減小一項。可是因爲v-for循環的是子組件,子組件內部顯示數據並未綁定selectionConditionList數組裏的屬性,所以子組件的顯示數據並未按新數組從新渲染,體現出來的結果就是最後一個元素被刪掉了。
解決方法就是給:key賦予一個獨一無二的值,這樣綁定的數組就能夠和dom對象一一對應起來,刪除的時候也能正確刪除掉響應dom對象了。綁定這個「獨一無二」的值,其中一個方法就是使用guid,也就是Global Unique Identifier,因而把生成guid的方法寫到了一個公共的js文件裏,:key綁定guid值,測試ok,大功告成!
代碼以下:
父組件:測試

<template>
    <div class="content-body">
        <div>任務</div>
          <div>
            <ul>
              <li v-for="(item,index) in selectionConditionList" :key="item.guid" style="margin:10px 0">
                <v-selection-condition-list></v-selection-condition-list> <button @click="deleteSelectionCondition(index)">刪除</button>
              </li>
            </ul>
          </div>
          <div>
             <button @click="addNewSelectionTask">新建任務</button>
          </div>
    </div>
</template>

<script>
/* eslint-disable */
import Utils from '../utils/utils.js'
import vSelectionConditionList from './SelectionConditionList'
  export default
  {
    data()
    {
      return {
        selectionConditionList:[],
      }
    },
    methods: {
      // 添加新的用戶篩選條件
      addNewSelectionTask(){
        this.selectionConditionList.push({guid:Utils.guid()});
      },
      // 刪除用戶篩選條件
      deleteSelectionCondition(index){
        console.log("delete.."+index);
        this.selectionConditionList.splice(index,1);
      }
    },
    components:{
      vSelectionConditionList
    }
  }
</script>
<style>
  .div_center {
    text-align: center;;
    width:100%;
    margin:0 auto;
  }
</style>

子組件:ui

<template>
  <input type="text" :value="inputName">
</template>

<script>
/* eslint-disable */
  export default
  {
    data()
    {
      return {
        inputName: Math.random()
      }
    },
    methods: {
    }
  }
</script>

guid方法:this

/* eslint-disable */
var utils = {
  guid: function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random() * 16 | 0,
        v = c == 'x' ? r : (r & 0x3 | 0x8);
      return v.toString(16);
    });
  }
}
export default utils

後話

此問題只出如今v-for嵌套子組件的狀況下。若是是v-for循環一個div或表單對象,而對象中的數據都是經過數組中的對象屬性綁定的,那麼數組刪除其中一項後,dom對象列表也能夠相應正確渲染。有興趣的話能夠把子組件換成input對象,而後在selectionConditionList中添加相似{val:Math.random()}這樣的數據,實測刪除後是沒有問題的。spa

相關文章
相關標籤/搜索