Do not update after treading pit vue v-for operation DOM

Keywords: Front-end Vue npm github

Do not update after treading pit vue v-for operation DOM

Preface

Recently, when I read the style guide for vue, I found a problem that I had previously ignored in learning or even developing.

Picture Description

phenomenon

Looking at the above paragraph, I think I have encountered similar problems when I was just developing.It is after forcibly modifying the DOM (such as changing the class); deleting the changed item from the v-for template array and finding that the class is still there.The extended scene is to select one or more lists, change the style (indicating that the list is selected), and delete the selected categories.The style is still found at this time.The description is not very clear, just above.

Picture Description

At this point, the first and second articles are selected, and after the point is deleted, the following figure is shown:

Picture Description

You will find that the color of the marker is still there and appears on both.

Take a look at the overall code (for ease of reading, just the key code follows, see the full code) https://github.com/goded-v/go...)

<!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()">delete</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(){// The algorithm is low, please understand
                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>

Scheme One

This effect is definitely unacceptable. As a code genius, I came up with a solution.

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;
    })

}

The list array is assigned null when rendering assignments, and then after the view is updated.This will achieve the desired effect.

Picture Description

Option 2

After looking at the diff algorithm of Vue Virtual dom first, it is found that the update of virtual dom is faster with key value index.(There are many online blogs with algorithm rules, let's not say more here).Plus, when you see the styling guide, it emphasizes that you always use key with v-for.I think of the problem I encountered before, is it caused by not having a bound key value?

So I wrote a demo and tried it.

<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()">delete</div>
</div>

The code is the same as the first complete code except that the key is bound.I found that I really achieved the desired result.

Option 3

Binding class es on v-for solves this problem by initializing attributes in the data to be responsive.

<!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()">delete</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>

This can also achieve the above effect.However, when we force a change in the dom, we will still find that the style is not updated as it was before.

<div @click="change()">Forced Change</div>
change(){
    document.getElementById("app").childNodes[0].childNodes[0].className = " click";
},

Picture Description

This is also due to the forced operation of the dom.If the key value is bound, you will find that the problem is resolved.

summary

In modules that use v-for rendering, a key value is required if dom is to be manipulated violently.However, to standardize development, use v-for with a key.The index of the key is not an index, but a unique property.

Posted by Hamlets666 on Fri, 10 May 2019 23:53:03 -0700