Vue 列表复用问题

Update 2019.2

最近研究了一下Vue的patch源码 & 看了网上的一些解析,对这个问题的理解更透彻了一点。

简单来说,对于如下数据

old:       1  2  3  4
new:     2  3  4  5

vnode的diff会优先把new[0] new[length-1]分别和 old[0] old[length-1] 比较。
如果有相等的:
直接进行移动操作(insertBefore),注意都是移动old对应的DOM。

如果都不相等:
(1) 在有key的情况下,
new[0] 从 old数组中找到了 old[1],认定是一个节点,可以diff,更新完内部可能的变更(例如文本什么的)后,于是把old[1]对应的DOM移动到old[0]前方(insertBefore),并删除old[1] = undefined
此后再处理 new[1] ,依次类推。
如果从 old数组中找不到,直接新建DOM
(2) 在没有key的情况下,直接新建DOM

所以可见,我们对数组进行了一次shift和一次push,看似只新增了一个元素,但是旧的DOM都经历了一遍 insert 的过程,对于有动画的DOM来说,动画就是会被再重新执行一遍。

我这里记得比较简略,想详细了解的,可以参考源码 & 文章:
Vue Patch.js
解析vue2.0的diff算法

------------------------- 以下是原文:

前言

最近在写一个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的源码还是一件很有必要的事情...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354