小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
前言
diff 作为 Virtual DOM 的加速器,其算法上改进优化是 React 整个界面渲染的基础和性能保障。diff会帮助我们计算出 Virtual DOM 中真正变化的部分,并只针对该部分进行原生 DOM 操作,而非重新渲染整个页面,从而办证了每次操作更新后页面的高效渲染。
diff 策略
- 策略一:
Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计 - 策略二:拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
- 策略三:对于同一层级的一组子节点,它们可以通过唯一的
id进行区分
基于以上策略,React分别对 tree diff、component diff、element diff 进行算法优化。
tree diff
React 对树进行分层比较,两棵树只会对同一层次的节点进行比较。React 通过 updateDepth 对 Virtual DOM 树进行层级控制,只会对相同层级的 DOM 节点进行比较,即同一个父节点下的所有节点。当发现节点已经不存在时,则该节点及其子节点会被完全删除,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。
如果出现跨层级的移动怎么办呢?
React 只会简单地考虑同层级节点的位置变换,而对于不同层级的节点,只有创建和删除操作。
看下图 节点 A 移动到 节点 D 之后
- 发现 节点A 消失,直接销毁 A
- D 节点发现多了一个 A 节点,则会创建 A(包括子节点)作为其子节点。
此时,diff 的执行情况:
creat A -> creact B -> create C -> delete A
React 官方建议不要进行 DOM 节点跨层级的操作
component diff
- 如果是同一类型的组件,按照原策略继续比较
Virtual DOM树即可 - 如果不是,则将该组件判断为
dirty component,从而替换整个组件下的所有子节点。 - 对于同一类型的组件,有可能其
Virtual DOM没有任何变化,如果能够确切知道这点,那么就可以节省大量的 diff 运算时间。因此,React 允许用户通过shouldComponentUpdate()来判断该组件是否需要进行 diff 分析。
看下图:
当组件
D 变化为 G 时,即使两个组件结构相似,一旦 React 判断 D 和 G 是不同类型的组件,就不会比较二者的机构,而是直接删除 D,重新创建组件 G及其子节点。
element diff
当节点处于同一个层级时,diff 提供了 3 种节点操作,分别为 INSERT_MARKUP(插入)、MOVE_EXISTING(移动)和REMOVE_NODE(删除)。
INSERT_MARKUP:新的组件类型不在旧集合里,即全新的节点,需要对新节点执行插入操作。MOVE_EXISTING:旧集合中有新组件类型,且element是可更新的类型,就需要做移动操作,可以复用以前的DOM节点。REMOVE_NODE:旧组件类型,在新集合里也有,但对应的element不同则不能直接复用和更新,需要执行删除操作,或者旧组件不在新集合里的,也需要执行删除操作。
对同一层级的同组子节点,添加唯一
key进行区分,从而提升 diff 性能
diff 差异化对比后,通过 key 发现新旧集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将旧集合中节点的位置进行移动,更新为新集合中节点的位置,diff 结果为:B、D 不做任何操作,A、C进行移动操作。
结语
如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏