最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue VNode更新方式引发的BUG

    正文概述 掘金(flyingbird)   2021-04-18   521

    近几日在调试一些“祖传代码”,遇到了vue更新使用VNode方式时带来的一些问题,于是乎费劲“九牛二虎”之力,终于找到了问题的根源,以后在此类代码设计中也算是增加了点经验,避免再次踩坑。项目中所使用的Vue版本:

    Vue.js v2.6.12

    问题背景

    由于原本的业务代码经过不断“迭代”已经比较复杂,相关操作逻辑散落在“代码海洋”的各处,这里简单描述一下问题的场景。

    组件在一个区域内可以自由拖动位置,有以下两种方法可以改变组件的位置

    • 鼠标移动组件
    • 通过快捷方式将组件快速移动到特定位置

    使用简单的示意图

    Vue VNode更新方式引发的BUG

    红色区块可以通过鼠标进行移动,也可以通过右边两个按钮可以分布直接定位到左上角、右上角。业务场景经过简化后,其实是比较简单和清晰的,但是真实情况中,由于各个业务逻辑耦合,导致各个代码功能耦合,还是比较难以直接定位问题关键原因的。

    问题现象

    通过鼠标移动红色区块后,再次通过右边两个快捷按钮进行定位,无法定位到左上角或者右上角,只能实现靠左以及靠右定位的效果。有兴趣的可以自己操作以下尝试一番,代码链接

    问题原因

    首先可以确认的是点击右边按钮之后,vuex中的属性值被改变成了期望值

    这点可以借助与vue的调试工具来证明

    Vue VNode更新方式引发的BUG

    这时我们可以再看一下代码中样式渲染逻辑

    <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值:

    Vue VNode更新方式引发的BUG

    基本上可以看出来问题所在了,VNode中的top值仍然是0,并不是当前DOM真实的top值

    问题根因

    为什么VNode中的值和DOM中的真实值不一致呢,这时应该就要考虑出了通过Vue响应式原理更新DOM属性值,是不是还有其他地方通过其他方式更新了DOM属性值?

    很明显,在组件拖动过程中,我们通过直接改变DOM的值改变了DOM的属性状态,但是并没有改变vuex中style的值,造成了VNode的状态和DOM中状态不一致的结果。

    解决方法

    在移动过程中通过改变vuex中style属性值来改变组件的样式,而不是通过直接改变DOM样式。

    思考:收敛更改状态的入口,可以在日常业务的代码海洋中减少此类问题的出现频率。


    起源地下载网 » Vue VNode更新方式引发的BUG

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元