近几日在调试一些“祖传代码”,遇到了vue更新使用VNode方式时带来的一些问题,于是乎费劲“九牛二虎”之力,终于找到了问题的根源,以后在此类代码设计中也算是增加了点经验,避免再次踩坑。项目中所使用的Vue版本:
Vue.js v2.6.12
问题背景
由于原本的业务代码经过不断“迭代”已经比较复杂,相关操作逻辑散落在“代码海洋”的各处,这里简单描述一下问题的场景。
组件在一个区域内可以自由拖动位置,有以下两种方法可以改变组件的位置
- 鼠标移动组件
- 通过快捷方式将组件快速移动到特定位置
使用简单的示意图
红色区块可以通过鼠标进行移动,也可以通过右边两个按钮可以分布直接定位到左上角、右上角。业务场景经过简化后,其实是比较简单和清晰的,但是真实情况中,由于各个业务逻辑耦合,导致各个代码功能耦合,还是比较难以直接定位问题关键原因的。
问题现象
通过鼠标移动红色区块后,再次通过右边两个快捷按钮进行定位,无法定位到左上角或者右上角,只能实现靠左以及靠右定位的效果。有兴趣的可以自己操作以下尝试一番,代码链接
问题原因
首先可以确认的是点击右边按钮之后,vuex中的属性值被改变成了期望值
这点可以借助与vue的调试工具来证明
这时我们可以再看一下代码中样式渲染逻辑
<div ref="container" class="root">
<div ref="el" class="block" :style="style"></div>
</div>
也就是说,在vuex的style属性值更新后,Vue并没有能正确渲染组件el的样式,那究竟是什么原因造成这一结果呢???
Vue响应式原理
经过一番定位后,基本上可以确认是Vue在重新渲染组件样式时出了问题,组件的样式并没有按照我们的设定值进行渲染,因为渲染出来的组件样式,与我们设置的style值并不一致, 此时通过chrome调试功能查看组件的样式,可以看到组件的样式为:
<div data-v-160ac6a1="" class="block" style="inset: 38px auto auto 0px;"></div>
组件的top值被渲染成了38px,并不是我们所期望的0。
此时我们不得不深入Vue的响应式中进行查看,正如大家所知道的那样,组件的渲染其实是一个大的Watcher,在相应状态改变后,会调用这个Watcher对页面进行渲染,给各个组件设置正确的状态。
而在组件的渲染中,Vue为了减少渲染的工作量,尽量避免渲染不需要改变的节点,使用了VNode的概念,每个VNode与真实渲染的DOM一一对应,在渲染真实的DOM之前,会对更新前后的VNode状态进行比较,如果更改前后的VNode状态是一样的,Vue就会跳过这个VNode对应DOM的更新。
VNode更新
于是,我们就是查看Vue更新Style的函数,由于代码较多,这里省略一些分支判断逻辑和注释,关注代码主干逻辑。
function updateStyle (oldVnode, vnode) {
var data = vnode.data;
var oldData = oldVnode.data;
var cur, name;
var el = vnode.elm;
var oldStaticStyle = oldData.staticStyle;
var oldStyleBinding = oldData.normalizedStyle || oldData.style || {};
// ...
var oldStyle = oldStaticStyle || oldStyleBinding;
var newStyle = getStyle(vnode, true);
for (name in oldStyle) {
if (isUndef(newStyle[name])) {
setProp(el, name, '');
}
}
for (name in newStyle) {
cur = newStyle[name];
if (cur !== oldStyle[name]) {
setProp(el, name, cur == null ? '' : cur);
}
}
}
在setProp之前,会对更新前后的VNode上的style值进行比较,如果更新前后VNode的值相同就会跳过这个属性的更新。
通过chrome调试控制台查看newStyle和oldStyle值:
基本上可以看出来问题所在了,VNode中的top值仍然是0,并不是当前DOM真实的top值
问题根因
为什么VNode中的值和DOM中的真实值不一致呢,这时应该就要考虑出了通过Vue响应式原理更新DOM属性值,是不是还有其他地方通过其他方式更新了DOM属性值?
很明显,在组件拖动过程中,我们通过直接改变DOM的值改变了DOM的属性状态,但是并没有改变vuex中style的值,造成了VNode的状态和DOM中状态不一致的结果。
解决方法
在移动过程中通过改变vuex中style属性值来改变组件的样式,而不是通过直接改变DOM样式。
思考:收敛更改状态的入口,可以在日常业务的代码海洋中减少此类问题的出现频率。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!