表格表头拖拽后,串行了,原来是“key”
前言
Vue中key的作用是什么?这个问题大家应该都不陌生。当然,我也接触过,但是还停留在接触这个阶段,当真正遇到问题的时候,差点栽跟头。
我遇到的问题
近几天,有一个表格表头拖拽的需求,如图所示,右侧自定义列的字段是表格中正展示的字段,右侧自定义列中的字段可拖拽,同时在左侧的表格中同步拖拽后的顺序。自身在实践的过程中发现,右侧拖拽后,左侧表格中的显示有问题,比如标题和创建时间位置互换,但是表格中渲染出来的第一列表头是标题,第二列还是标题,下面对应的内容是对的,那这个问题的原因是什么呢?先来看看我的代码实现吧。
表格拖拽代码
表格代码:
<div class="table-div"> <elable ref="table" v-loading="loading" :data="data" v-bind="tableProps" style="width: 100%;" :row-class-name="rowClassName" @header-dragend="handleDrag" @row-dblclick="dblclickRow" @sort-change="handleSortChange" @selection-change="onSelectionChange" > <template v-if="columns.length > 0"> <el-table-column v-for="(column, index) in columns" :key="index v-bind="column" :min-width="column.minWidth || 140" :resizable="true" :show-overflow-tooltip="true" /> </template> <slot v-else /> </el-table> </div> 复制代码
自定义列拖拽组件封装代码:
<div class="display-field-wrapper"> <el-popover ref="display-field-popover" placement="bottom" width="216" popper-class="custom-field-popover" trigger="click" > <div class="display-field__popover-panel"> <div class="screening-box"> <el-select v-if="showGroup" v-model="typeValue" class="display-field__search" placeholder="全部字段类型" > <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </div> <div class="display-field__title"> <span class="select-title">{{ enableLabel }}</span> <span class="select-counts">{{ fieldCheckedList.length +'/'+ fieldLen }}</span> </div> <div class="display-field__checked-list"> <draggable v-model="enableFieldsFilter" :draggable="draggableClass" @sort="onSort" > <div v-for="item in enableFieldsFilter" :key="item.prop" class="display-field__check-item" :class="{ 'display-field__draggable-item': item.can_cancel }" > <el-checkbox v-model="fieldCheckedList" :label="item.label" :disabled="!item.can_cancel" @change="handleCheck(item, -1)" /> </div> </draggable> </div> <div class="display-field__title"> <span class="select-title">{{ notEnableLabel }}</span> <span class="select-counts">{{ (fieldLen - fieldCheckedList.length) +'/'+ fieldLen }}</span> </div> <div class="display-field__unchecked-list"> <div v-for="item in notEnableFieldsFilter" :key="item.prop" class="display-field__check-item" > <el-checkbox v-model="fieldCheckedList" :label="item.label" :disabled="!item.can_cancel" @change="handleCheck(item, 1)" /> </div> </div> </div> </el-popver> </div> 复制代码
其实当时我在排查的时候,首先在组件中排查,以为拖拽有问题,但是实际上,内容是对的,只是表头有问题,这才想起之前的一个知识点,说实话,这个点早就知道,但是自己也确实没遇到过类似问题,所以也没在意。一般我在写for循环的时候,都会像上述代码中那样写,key值就写个index,一直没遇到过问题。哈哈。
key的真实作用
key是虚拟DOM对象的标识,当数据发生变化时,vue会根据新数据生成新的虚拟DOM,随后VUe进行新虚拟DOM与旧虚拟DOM的差异比较。比较规则是什么呢?
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key: 若虚拟DOM中内容没变,直接使用之前的真实DOM;若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key,则创建新的真实DOM,随后渲染到页面中。
问题解决
那上面我的问题就应该有答案了。我是用index值作为的key,那当我拖拽之后,在进行对比的过程中,发现索引值没变,那干脆内容也不变了。后来我将key值加index的同时,还加上了一个会变化的字段,那就是列的定义字段,这样就能保证在进行diff算法的过程中,会有变化啦。
总结
伪原创工具 SEO网站优化 https://www.237it.com/
在日常开发中个,尽量选择每条数据的唯一标识作为key,比如id等,避免出现类似问题;如果不对数据的逆序添加、删除等破坏顺序的操作,仅仅用于列表渲染,其实使用index作为key是没有问题的。
Happy Ending.
作者:Delicia_Lani
链接:https://juejin.cn/post/7035450586417856520