然后就是如果prevChildren存在的时候,也就是render第二次触发的时候遍历旧的children列表,首先会把最新的transition属性更新到旧的VNode上,然后就是很关键的去获取VNode对应的DOM节点的位置(很重要!),并且记录;然后再根据map判断哪些VNode是需要保持的(新旧列表相同的VNode),哪些是需要移除的,最后就是把this.kept指向需要保持的VNode列表;所以this.kept在第一阶段的pacth过程中,才能准确把要移除的VNode先移除,并且不会插入新的VNode,也不会移动DOM节点;在执行后面的update方法才会做后面两步。
接着看updated方法,如何去利用FLIP实现移动动画的尼:
updated () { const children: Array<VNode> = this.prevChildren const moveClass: string = this.moveClass || ((this.name || 'v') + '-move') if (!children.length || !this.hasMove(children[0].elm, moveClass)) { return } // we divide the work into three loops to avoid mixing DOM reads and writes // in each iteration - which helps prevent layout thrashing. children.forEach(callPendingCbs) children.forEach(recordPosition) children.forEach(applyTranslation) // force reflow to put everything in position // assign to this to avoid being removed in tree-shaking // $flow-disable-line this._reflow = document.body.offsetHeight children.forEach((c: VNode) => { if (c.data.moved) { const el: any = c.elm const s: any = el.style addTransitionClass(el, moveClass) s.transform = s.WebkitTransform = s.transitionDuration = '' el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { if (e && e.target !== el) { return } if (!e || /transform$/.test(e.propertyName)) { el.removeEventListener(transitionEndEvent, cb) el._moveCb = null removeTransitionClass(el, moveClass) } }) } }) },
这里的处理首先会检查把move class加上之后是否有transform属性,如果有就说明有移动的动画;再接着处理:
调起pendding回调,主要是移除动画事件的监听
记录节点最新的相对位置
比较节点新旧位置,是否有变化,如果有变化就在节点上应用transform,把节点移动到旧的位置上;然后强制reflow,更新dom节点位置信息;所以我们看到的列表可能表面是没有变化的,其实是我们把节点又移动到原来的位置上了;
最后我们把位置有变化的节点,加上move class,触发移动动画;
这就是transition-group所拥有的黑魔法,确实帮我们在背后做了不少的事情。
最后
温故而知新,在写的过程中其实发现了以前的理解还是有很多模糊的地方,说明自己平时阅读代码仍然不够细心,没有做到不求甚解,以后必须多多注意,最后的最后,如有错漏,希望大家能够指正。