问题
当使用了Sortable.js这种第三方库,拖拽之后,视图更新不正确。
更准确的说,在template中绑定的是questionSettings.pages,当拖拽操作的数据层面改变了questionSettings.pages,那么会触发视图更新,结果视图更新的结果不正确。
解决问题的尝试
我之前找过Sortable.js的Github,大致看了一下,只发现Sortable.js只说明了各个hook函数,并没有说明视图更新的问题。
在issue中也查找了视图更新的问题,确实有一些视图更新的反馈问题,但是官方其实更想要从Sortable.js本身来解决问题,视图更新的问题出现后,只能将这个bug作为下一个发布的版本前必须解决的问题,就我现在用的版本,不会解决这个问题。
而同样使用Sortable.js的跟踪回答视图更新问题的人,他们并没有提供一个直接适合我使用的场景的解决方案。
所以,在初步探索Sortable.js的Github之后,并没有找到直接的解决方案。
我认为查看第三方库的Github这个手段更适合用来切实的了解这个组件向外暴露的接口和属性,在版本更新之后,是否有变化。如果你正确使用了该组件,在具体的业务步骤上出现了bug,那么大概率上,查看第三方库的Github并不能让你找到解决bug的方法。
正确的解法
问题分析
既然在拖拽后,自动更新视图的结果不正确。是重新渲染拖拽后questionSettings.pages
这一步出了问题,那么将问题定位在数据层面。
不再执行自动更新,而是为了排除数据层面引发错误的因素,强制赋值为空,渲染空数组,然后再渲染拖拽后的数据。
实现解决方法
先保存拖拽后的questionSettings.pages,使用深拷贝确保引用变化。
const newPages = JSON.parse(JSON.stringify(questionSettings.pages));
强制赋值为空,渲染空数组。
questionSettings.pages = [];
视图渲染即DOM更新操作,在Vue中DOM更新操作属于微任务,需要在同步代码执行完后执行,所以在Vue检测到数据变化之后,不会立即执行更新操作,而是将更新操作加入更新队列。
我的目的是,在视图渲染为空数组后,渲染深拷贝的拖拽后的questionSettings.pages。使用nextTick这个hook函数,在DOM更新之后执行的函数。nextTick这个回调函数也是一个微任务,所以微任务队列中,前有DOM更新操作,后有nextTick回调函数,这样就满足了我在执行顺序上的要求。
nextTick的回调函数中只要简单的一句
questionSettings.pages = newPages
就能完成重新渲染拖拽后的questionSettings.pages。
全部代码如下:
const newPages = JSON.parse(JSON.stringify(questionSettings.pages));
questionSettings.pages = [];
nextTick(() => {
questionSettings.pages = newPages
});
执行顺序分析
- 同步代码执行完毕
- Vue 开始处理更新队列,执行 DOM 更新(此时页面确实会渲染为空)
- DOM 更新完成后,执行
nextTick回调 - 在回调中设置新的 pages 数据,触发新一轮更新
注意
实际上页面是会短暂地渲染为空的,只是这个过程非常快,可能肉眼难以察觉。 nextTick 并不会阻止空数组的渲染,而是在空数组渲染完成后才执行。