在虚拟dom中diff的实现。

分别从3个方面:

  1. DIFF抽象概念(概述、时间复杂性分析、对比总结)
  2. 在Vue2中的实现(版本2.6.11、必要性、执行方式)
  3. 在React中的实现(版本16.13.1,必要性、执行方式)

1. DIFF抽象概念

diff是广泛的概念,如git diff,js对象 diff等。两棵树做diff,即虚拟DOM中的diff算法。

diff算法的必要性:渲染真实DOM的开销是很大的,轻微的操作都可能导致页面重新排版,非常耗性能。 相对于DOM对象,js对象处理起来更快,而且更简单。 通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行 dom操作,从而提高性能。

 

diff时间复杂度分析:

常规:O(n^3) 遍历树1; 遍历树2; 排序; 1000个节点,十亿的量级。

优化:O(n) 只比较同一层级 ;tag不相同,直接删掉重建; 通过key来标识区分相同节点。

 

1. 只比较同一层级。

Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计。

difflib的SequenceMatcher底层用了什么算法_js对象

difflib的SequenceMatcher底层用了什么算法_树形结构_02

difflib的SequenceMatcher底层用了什么算法_js对象_03

如图左,只比较同一层级的,即相同颜色的层级。如右图,左侧P3在右侧的P标签下,不做移动,而是做新增操作和删除操作。

 

2.tag不相同,直接删掉重建。

拥有不同类型的两个组件将会生成不同的树形结构。

difflib的SequenceMatcher底层用了什么算法_Vue_04

如上图,D和G节点的子节点相同,但是tag不同,则整棵树都重新删除重建。

 

3.通过key来标识区分相同节点。

开发者可以通过 key prop 来暗示哪些子元素在不同的渲染下能保持稳定。

difflib的SequenceMatcher底层用了什么算法_树形结构_05

difflib的SequenceMatcher底层用了什么算法_js对象_06

左图没有key值,所以老的节点全部删除,新的节点再全部创建。右图添加key值,所以只需要酱A移动到B,将C移动到D,就可以得到与新树一样的老树。

 

对比

相同点: 深度优先、同层级比较。 tag不同默认不同。 都借助key。 都有批量新增和删除等。

不同点: 架构不同,React基于fiber链表结构实现,不能双向查找。Vue基于数组,可以很方便通过下标查找。 react的设计一开始就有,vue演进过程中才出现的。

diff算法总结(5w1h)

what: 是一种对新旧虚拟DOM树进行比较,得出两者差异,确定最小DOM更新操作的算法。

why: 减少渲染真实DOM的开销,提高性能。

When: 页面更新,重新渲染时用到。

where: Vue中当数据发生改变时,set方法会让调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁。React在下一次 state 或 props 更新时, render()方法会返回一棵不同的树。在构建fiber时,标记effectTag为Placement、Update、Deletion等。在commitWork构建真实DOM时,按照effectTag规则生成DOM。

Who: Vue(patch、patchVnode、updateChildren)。React(reconcileChildFibers、reconcileChildrenArray、updateHostComponent等)。 how: 深度优先、同层比较。tag不同,生成树形结构不同。使用key来标识稳定节点。