前言
最近因为做了公司一个表格拖拽排序的需求,因而接触到了sortableJs这一插件,现在在此作一记录。
正文
使用
- 官方文档
- 代码环境
- element-plus
- vue 3
// 给相关表格绑定对应的Sortable使其可拖动
const rowDrop = () => {
tableList.list.forEach(item => {
// 此时找到的元素是要拖拽元素的父容器
const table = document.querySelector('#table' + item.senceId)
const tbody = table.querySelector('tbody')
// Sortable.create需要传入表格body的DOM元素,如上利用querySelector查找到该DOM元素并传入
const a = Sortable.create(tbody, {
// 列表数据
tableDate: item,
// 指定父元素下可被拖拽的子元素
draggable: '.el-table__row',
// 结束拖拽触发事件
onEnd ({ newIndex, oldIndex }) {
// 可以在此对数组数据进行处理
changeDate(this.tableDate, newIndex, oldIndex)
}
})
a.tableDate = toRaw(item)
})
}
踩坑
- 但是在使用过程中却出现了问题
- 我在这个项目中的原本流程是:
- 表格某行被拖拽->触发数据更新并将其更新数据上传给后端->向后端查询更新表格数据
- 表格的数据拖拽过几次会出现表格数组顺序与表格渲染顺序不一致的情况
- 上网查找了一下资料,发现已经有人踩过这个坑了
- 踩坑文章
- 可以得知是vue的虚拟Dom和真实Dom不一致导致的问题
- sortableJs这一插件是直接交换了真实Dom的位置来达到表格拖拽的效果
- 但是对原本的表格数组的数据不会有变更,需要自己处理
- 真实Dom的位置改变了,但是Vue的虚拟Dom没有改变,因此当表格数据更新时,Vue会把数据渲染到错误的Dom位置而导致表格数据乱序
- 因此在使用像Vue这样的MVVM框架时,最好还是不要直接操作Dom,否则就有可能与Vue自己的数据绑定机制起冲突,造成像我这样的问题
- 解决这个问题的思路也比较简单,就是在真实Dom改变后,主动通知Vue更新它的虚拟Dom,与其保持一致,这样就不会有问题了
- 一个比较简单且暴力的方法就是基于v-if重新渲染相应部分的Dom,在拖拽结束后就重新渲染一遍
- 当然缺点也是很明显,会消耗更多的性能,并造成页面部分消失一小段时间,是一个最省事但效果最差的方法
- 手动还原真实Dom的操作,具体见上面的那篇踩坑文章,比较麻烦
- 换插件
- 直接换一个兼容vue并自带数据更新的插件,那就是vue.draggable,vue.draggable也是基于SortableJs开发的,对Vue框架做了兼容和支持工作