最近在看vue的風格指南時,發現了一個之前在學習,甚至開發時忽略的問題。javascript
看到上面的一段話,想到本身在剛開發的時候遇到過相似的問題。就是在強行修改DOM後(好比改變class);將v-for 模板數組中,改變的這條刪除掉,發現,該class還在。延伸到的場景就是先選擇一個或者多個列表,改變了樣式(代表該列表被選中),再刪除選中的類別。此時發現該樣式還在。描述的不是很清楚,直接上圖。html
此時選中了第一條和第二條,點刪除後以下圖:vue
這時就會發現,爲啥標記的顏色會還在,並且出如今了這兩條上面。java
先看下總體的代碼(爲了閱讀方便,後面只是關鍵代碼,完整代碼見https://github.com/goded-v/go...)git
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> .click{ background: red; } </style> <body> <div id="app"> <div v-for="(item,index) in list"> <p :class="{click:item.class}" @click="add($event,item)">{{item.name}}</p> </div> <div @click="deleteItem()">刪除</div> </div> </body> <script> var app = new Vue({ el:"#app", data:{ list:[ {name:"0",id:"0"}, {name:"1",id:"1"}, {name:"2",id:"2"}, {name:"3",id:"3"}, {name:"4",id:"4"}, {name:"5",id:"5"}, ], deleteArr:[], }, methods: { add(event,item){ let me = this; let target = event.currentTarget; target.className = target.className=="click"?"":target.className="click"; if(target.className==="click"){ me.deleteArr.push(item); }else{ let index = me.deleteArr.indexOf(item); if (index > -1) { me.deleteArr.splice(index, 1); } } }, deleteItem(){// 算法很low,請諒解 let me = this; let newArr = []; for (var i=0;i<me.list.length;i++){ if(me.deleteArr.indexOf(me.list[i]) === -1){ newArr.push(me.list[i]); } } me.list = newArr; } } }); </script> </html>
這種效果確定是不能接受的去,做爲代碼鬼才的我,想到了一個解決辦法。github
deleteItem(){ let me = this; let newArr = []; for (var i=0;i<me.list.length;i++){ if(me.deleteArr.indexOf(me.list[i])===-1){ newArr.push(me.list[i]); } } me.list = []; this.$nextTick(()=>{ me.list = newArr; }) }
在渲染賦值的時候先將list數組賦值爲空,在視圖完成更新後,再賦值。這時就能實現想要的效果。算法
先看了vue Virtual DOM 的 diff算法後,發現虛擬dom的更新在有key值索引的狀況下更快。(具備算法規則網上博客不少,在此很少說)。再加上看到風格指南上強調老是用key配合v-for。我就聯想到之前遇到的這個問題,是否是沒有綁定key值致使的。npm
因而就寫了個demo試了一下。數組
<div id="app"> <div v-for="(item,index) in list"> <p :class="{click:item.class}" @click="add($event,item)" :key="item.name">{{item.name}}</p> </div> <div @click="deleteItem()">刪除</div> </div>
代碼同第一份完整的代碼同樣,只是綁定了key。發現,真的達到了想要的效果。app
在v-for時綁定class來解決上述問題,將屬性初始化在data中,使之成爲響應式的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> .click{ background: red; } </style> <body> <div id="app"> <div v-for="(item,index) in list" > <p :class="{click:item.class}" @click="add($event,item)">{{item.name}}</p> </div> <div @click="deleteItem()">刪除</div> </div> </body> <script> var app = new Vue({ el:"#app", data:{ list:[ {name:"0",id:"0",class:false}, {name:"1",id:"1",class:false}, {name:"2",id:"2",class:false}, {name:"3",id:"3",class:false}, {name:"4",id:"4",class:false}, {name:"5",id:"5",class:false}, ], deleteArr:[], }, methods: { add(event,item){ let me = this; item.class = !item.class; me.deleteArr.push(item) if(item.class === true) { me.deleteArr.push(item) }else { let index = me.deleteArr.indexOf(item); if (index > -1) { me.deleteArr.splice(index, 1); } } }, deleteItem(){ let me = this; let newArr = []; for (var i=0;i<me.list.length;i++){ if(me.deleteArr.indexOf(me.list[i])===-1){ newArr.push(me.list[i]); } } me.list = newArr; } } }); </script> </html>
這樣也能實現上述效果。可是當咱們強行改變dom後,會發現,仍會出現剛纔那種,樣式不更新的現象。
<div @click="change()">強行改</div>
change(){ document.getElementById("app").childNodes[0].childNodes[0].className = " click"; },
這也是因爲強制操做dom形成的。若是綁定上key值,就會發現解決問題了。
在使用v-for渲染的模塊中,若是要暴力操做dom,須要key值。可是爲了規範開發,在使用v-for時都要帶上key。key的索引不要是index,最好是特有的屬性。