最近使用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