踩坑vue v-for操做DOM後不更新

踩坑vue v-for操做DOM後不更新

前言

最近在看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,最好是特有的屬性。

相關文章
相關標籤/搜索