小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
前言
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
进行移动操作。
结语
如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏