react 的 diff 算法
react 的 diff 算法
- 为什么会用到 diff 算法?
react在编译时将 jsx 解析为虚拟 dom 树,使用对象表示。在数据变动后会生成新的虚拟 dom 树对象,之后对比找出新旧虚拟 dom 树的差异,并将变动更新到浏览器页面上。
- diff 算法的时间复杂度
即使使用最前沿的算法,若要完整的对比两棵树,算法时间复杂度至少会达到O(n^3),在 react 的 diff 实践中,做了一些限制将 diff 算法的时间复杂度降低为O(n)
- react 中 diff 算法如何实现的?
-
为了降低算法的复杂度,React 为 diff 算法设置了 3 个限制:
- 只对同级别元素进行 diff,如果一个 DOM 元素在前后两次更新中跨越了层级,那么 React 不会尝试复用它
- 两个不同类型的元素会产生不同的树。比如元素从 div 变成了 p,那么 React 会直接销毁 div 以及子孙元素,新建 p 以及 p 对应的子孙元素
- 开发者可以通过 key 来暗示哪些子元素能够保持稳定
-
单节点 diff
单节点是指新节点为单一节点,但是旧节点数量不限制
单节点 diff 步骤:
- 判断 key 是否相同,若不同,结果直接为不能复用,若相同直接下一步
- 若key相同,在判断type是否相同,若type相同就复用,若不同标记为删除
-
多节点 diff
多节点指的是新节点有多个,旧节点个数不限制
多节点 diff 步骤:
多节点情况下,可能需要 【新增,删除,移动】节点,需要两轮遍历- 第一轮遍历:尝试逐个的复用节点
- 若新旧节点的key和type都相同,则复用
- 若新旧节点的key相同,但type不同,生成新的节点,旧节点增加删除标记(但不结束遍历)
- 若新旧节点的key不同,不可复用,结束遍历
- 第二轮遍历:处理上一轮遍历中没有处理完的节点
若第一轮遍历被提前终止了,意味着有新的react元素或旧的fiberNode没有遍历完,此时需要第二轮遍历,包括以下三种情况:
- 只剩下旧子节点:将旧的子节点标记删除统一删掉
- 只剩下新子节点:新的子节点直接创建fiberNode节点
- 新就节点都有剩余:会将剩余旧的子节点放入一个map里,遍历新的子节点,若找到能复用的直接拿来复用,若找不到新节点直接新增,旧节点标记删除
-