美文网首页
Vue 列表复用问题

Vue 列表复用问题

作者: NowhereToRun | 来源:发表于2019-01-06 23:08 被阅读32次

    前言

    最近在写一个Vue封装了一层的简单的弹幕组件时发现,Vue的数组v-for循环绑定了key,结果看起来好像还是并没有DOM复用。

    弹幕的原理不用多说,简单的css动画就能搞定

    .barrage-container .barrage-item {
      backface-visibility: "hidden";
      transform-style: "preserve-3d";
      display: inline-block;
      white-space: pre;
      position: absolute;
      left: 100%;
      max-width: 6.8rem;
      animation: flot 7000ms linear forwards;
    }
    
    @keyframes flot {
      from {
        transform: translate3d(0, 0, 0);
      }
      to {
        transform: translate3d(-1000px, 0, 0);
      }
    }
    

    更新弹幕的时候就是删除已经停止播放的DOM,防止页面DOM过多;再添加即将需要播放的DOM节点。
    反馈在Vue上,就是数组的shiftpush

    但是在执行的时候,发现一旦开始同时push和shif,那些正在执行中的动画,会从头再播一遍,造成奇怪的闪烁。观察页面结构发现,似乎每一个节点都被更新了一遍。

    寻找问题

    第一次尝试,记录更新DOM

    初始的时候,怀疑Vue的问题,导致每次都重刷了列表所有DOM,于是重写了document.createElement(),如下

    var oldCreate = document.createElement;
    
    document.createElement = function(...type) {
        console.log(type);
        return oldCreate.apply(document, type);
    }
    

    结果果然不是重刷了所有的DOM,每次其实都只有新建的元素
    打出来的log,只有新建的那几个DOM的日志。

    第二次尝试,断点调试

    Chrome给我们提供了在DOM变化时的断点,这个大大方便了开发人员进行调试,如下图。

    Chrome断点调试

    于是在原本不该重新执行动画的DOM上打断点,果然找到问题

    DOM重新插入
    原本不该被操作的DOM节点被重新插入。虽然还是同一个DOM节点,但是有了重新插入的这一次,就导致了动画重新执行。
    观察调用栈,可以发现问题就出在diff策略上。
    diff

    这种问题,对于Vue这个规模的插件,我肯定不是第一个踩坑的人,找到对应issue,
    【list rendering optimization fails in some cases #4362】

    解决问题

    说实话在进行上面两个尝试之前,我使用了万能的settimeout 0大法,其实就是把数组的pushshift不放在一次事件循环之中。因为在调试时候发现,命令行shift和push都不会有问题,于是就想简单的解决这个问题,发现这个方法在绝大部分手机上都没问题。

    (同时push和shift还有加延迟push,写了个demo,可以看一下区别 ==> jsfiddle

    直到测试同学发现在某些老旧安卓手机上(好像是华为Mate几忘了 Android 5.0的系统),弹幕出现了鬼畜闪烁,看了一下现象,就是DOM重新插入所致,机器版本太老也不好调试,只能下定决心彻底解决这个问题。

    最终解决方法
    发现官方维护人员在issue Inconsistent element reuse in v-for when using key 中提到了使用transition-group
    重新回去翻了一下文档,发现就是不好好看文档的锅😂....

    使用方法也很简单,直接在以前弹幕的外层套一层<transition-group></transition-group>
    Vue本身做了很多细微的操作,比如创建DOM的同时transition的触发,还有动画的删除过程,其实就是在他内部的生命周期开了更多的钩子,同时帮开发者在DOM上更换class。

    比如现在删除数组里一个元素,vue不会再直接干掉这个DOM了,而是会先给他一个即将删除的class,让你可以执行自己的动画,然后这个DOM才会消失。

    最终对着周期写样式就完事了....

    嗯...对于经常使用Vue开发业务的人员来说... 看vue的源码还是一件很有必要的事情...

    相关文章

      网友评论

          本文标题:Vue 列表复用问题

          本文链接:https://www.haomeiwen.com/subject/bqntrqtx.html