React -- Diff算法
Posted 我真的爱敲代码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React -- Diff算法相关的知识,希望对你有一定的参考价值。
欢迎学习交流!!!
持续更新中…
React中的diff算法是该框架的核心,React diff 会帮助我们计算出 Virtual DOM 中真正变化的部分,并只针对该部分进行实际 DOM 操作,而非重新渲染整个页面,从而保证了每次操作更新后页面的高效渲染。
传统 Diff 算法
在计算两颗树形结构差异的最优解一直是一个复杂的问题,传统的diff算法,需要遍历整棵树的节点然后进行比较,是一个深度递归的过程,运算复杂度常常是O(n^3),其中n是树中元素的个数,因此传统 diff 算法较为复杂
如果在React中使用传统diff算法,展示1000个元素,就需要10亿次的比较。于是 React 在以下两个假设的基础之上提出了一套 O(n) 的启发式算法:
- 两个不同类型的元素会产生出不同的树;
- 对于同一级的子节点,拥有同层唯一的key值,来做删除、插入、移动的操作,这是针对element层级的策略;即开发者可以通过设置 key 属性,来告知渲染哪些子元素在不同的渲染下可以保存不变;
- react不会跨层对比,只会同层对比
React Diff
tree diff
基于策略三,React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较。
既然 DOM 节点跨层级的移动操作少到可以忽略不计,针对这一现象,React 通过 updateDepth 对 Virtual DOM 树进行层级控制,只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。
component diff
React 是基于组件构建应用的,对于组件间的比较所采取的策略也是简洁高效。
- 如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
- 如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点
- 对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff。
如下图,当 component D 改变为 component G 时,即使这两个 component 结构相似,一旦 React 判断 D 和 G 是不同类型的组件,就不会比较二者的结构,而是直接删除 component D,重新创建 component G 以及其子节点。虽然当两个 component 是不同类型但结构相似时,React diff 会影响性能,但正如 React 官方博客所言:不同类型的 component 是很少存在相似 DOM tree 的机会,因此这种极端因素很难在实现开发过程中造成重大影响的。
element diff
当节点处于同一层级时,React diff 提供了三种节点操作,分别为:INSERT_MARKUP(插入)、MOVE_EXISTING(移动)和REMOVE_NODE(删除)。
- INSERT_MARKUP,新的 component 类型不在老集合里, 即是全新的节点,需要对新节点执行插入操作。
- MOVE_EXISTING,在老集合有新 component 类型,且 element 是可更新的类型,generateComponentChildren 已调用 receiveComponent,这种情况下 prevChild=nextChild,就需要做移动操作,可以复用以前的 DOM 节点。
- REMOVE_NODE,老 component 类型,在新集合里也有,但对应的 element 不同则不能直接复用和更新,需要执行删除操作,或者老 component 不在新集合里的,也需要执行删除操作。
总结
- React 通过制定的 diff 策略,将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题;
- React 通过分层求异的策略,对 tree diff 进行算法优化;
- React 通过相同类生成相似树形结构,不同类生成不同树形结构的策略,对 component diff 进行算法优化;
- React 通过设置唯一 key的策略,对 element diff 进行算法优化;
- 建议在开发组件时,保持稳定的 DOM 结构会有助于性能的提升;
- 建议在开发过程中,尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,在一定程度上会影响 React 的渲染性能。
以上是关于React -- Diff算法的主要内容,如果未能解决你的问题,请参考以下文章
ReactJS diff算法Part 2: React diff 启发式算法(heuristic algorithm)的假设