VUE学习|使用v-for和checkbox中遇到的问题

本文记录了我在实现一个简单的TodoList的过程当中遇到的问题即解决方法。因为我目前水平较低,仍有未明白的地方,同时文中也可能出现纰漏或者错误指出,如果有人能够看到此文,但愿能够解答个人疑惑或者指出不正之处,谢谢。html

需求

在一个简单的TodoList应用中,使用v-for指令绑定待办事项列表进行渲染。待办事项列表中每一个对象有一个Boolean类型的属性代表该事项是否已经完成,在页面中经过v-model将该属性与一个checkbox进行双向绑定,实现经过勾选设置事项是否完成。数组

如今要实现的是将用户勾选标明已完成的事项放到下面,也就是在点击checkbox后调整数组的位置,实现全部的已完成事项都出如今未完成事项的下面。同时,若是从新将已完成的事项取消勾选,其又会上升。函数

本来的作法以下:性能

<ul>
    <li v-for="item in todoList">
        <input type="checkbox" v-model="item.isFinished" @change="moveEvent(item, $event)">
        <span :class="{finished: item.isFinished}">{{ item.name }}</span>
        <button @click="removeEvent(item)">删除</button>
        <button @click="topEvent(item)">置顶</button>
    </li>
</ul>
复制代码

moveEvent()函数以下,作法比较粗暴,先从数组中移除原来的对象,而后找到合适的位置再加进去。ui

moveEvent(item, event){
    console.log(event.target.checked)
    if(event.target.checked){
        this.todoList.splice(this.todoList.indexOf(item), 1);
        var index;
        for(index = 0; index < this.todoList.length; index++){
            if(this.todoList[index].isFinished == true){
                break;
            }
        }
        this.todoList.splice(index, 0, item);
    }else{
        this.todoList.splice(this.todoList.indexOf(item), 1);
        var index;
        for(index = 0; index < this.todoList.length; index++){
            if(this.todoList[index].isFinished == true){
                break;
            }
        }
        this.todoList.splice(index, 0, item);
    }
}
复制代码

出现的问题

初始状态this

当我点击列表的第一个元素的checkbox时,出现以下图的状况:spa

发现,虽然第一个事项是被移到了下面,而且成功修改成已完成,可是随后因为修改了数组产生的新的第一个元素的checkbox也被勾选上了,可是没有出现已完成的CSS效果,而且刷新后就能够正常显示,说明其实际上并无被修改成已完成。双向绑定

一样的,取消勾选会致使相反的状况,一样刷新后即会正常显示。code

缘由分析

能够判断咱们的数据并无出错,而且数组元素的移动也是正常的,可是咱们的checkbox明明经过v-model进行了双向绑定,为何会出现显示错误的状况呢?通过定位,最终将问题锁定在v-for指令上。先来看看官网对于v-for指令的介绍:cdn

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。若是数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每一个元素,而且确保它们在每一个索引位置正确渲染。这个相似 Vue 1.x 的 track-by="$index"。

其实这也是一个老生常谈的问题了,就是说当咱们改变了数据项顺序,Vue并无移动DOM元素,而是从新渲染每一个DOM上的数据,这样的目的应该是为了减小DOM操做,提升效率。

那么回看咱们的问题,大体判断缘由应该就是出于此:假设咱们把当前索引为0的元素标记为已完成,该元素被移到数组下方,而且新位置上渲染结果正确。可是此时因为从新排序而出如今索引0位置的新元素的checkbox却因为DOM重用的结果,没有获得更新,后面的解决方案也验证了是用于这个缘由形成的。

可是我是进行了v-model绑定了的,为什么仍是会出现这种状况我尚未彻底明白,在网上查了也没有类似状况,如有看到此处的人明白的,还请不吝赐教

解决方案

对应的,Vue官网给出了解决方法:

为了给 Vue 一个提示,以便它能跟踪每一个节点的身份,从而重用和从新排序现有元素,你须要为每项提供一个惟一 key 属性。

建议尽量在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容很是简单,或者是刻意依赖默认行为以获取性能上的提高。

那么尝试将v-for改成:

<li v-for="(item, index) in todoList" :key="index">
复制代码

结果发现,仍是不行。我认为缘由应该是这样的写法用数组的地址做为元素的身份,那么原先被标记为0(即数组中索引为0的元素)被成功勾选并下移,而新的数组头元素此时也被标记为0,二者身份相同,因此又会错误的被勾上!

正确写法(错误写法)以下,这里我使用了元素的name属性,即事件的名称:

<li v-for="item in todoList" :key="item.name">
复制代码

尴尬😅,原本觉得上面的写法是正确的,可是在写本文的时候写着写着忽然以为不对,既然索引会出现错误,那么具备相同name属性的事件是否是也会出现错误?去试验了一下,果不其然...但仍是记录下来给本身提个醒。

那么,通过这么屡次试验得出的结论就是须要使用惟一的key属性,才能保证万无一失,例如给每一个事件一个不会重复的id

相关文章
相关标签/搜索