diff(协调)算法
React diff算法的三个策略
1. 策略一:WEB UI中跨层级的移动操作特别少,可忽略不记
2. 策略二:用于相同类的两个组件将会生成相似的树形结构,拥有不用类的两个组件将会生成不用的树形结构
3. 策略三:对于同一层级的不同子节点,我们可以通过唯一id进行区分
基于以上三个策略,React 分别对 tree diff、component diff 以及 element diff 进行算法优化
1. tree diff
对树进行分层比较,两棵树只会对同一层级的节点进行比较。DOM节点跨层级移动忽略不记,React通过`updateDepth`对`Virtual DOM `进行层级控制,只对相同层级的DOM节点进行比较,即同一父节点下的所以子节点,当发现该节点已经不存在时,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个DOM树的比较。
**如果出现了 DOM 节点跨层级的移动操作,diff 会有怎样的表现呢?**
- 当出现节点跨级移动时,并不会像想象中的那样执行移动操作,而是以 A 为根节点的整个树被整个重新创建,这是影响 React 性能的操作,所以 官方建议不要进行 DOM 节点跨层级的操作。
> 优化点:在开发组件中,保持稳定的 DOM 结构有助于性能的提升。例如,可以通过CSS隐藏或显示节点,而不是真正的移除或添加 DOM 节点。
2. component diff
- 如果是同一类型的组件,按照原策略继续比较 Virtual DOM 树即可
- 如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点
- 对于同一类型下的组件,有可能其 Virtual DOM 没有任何变化,如果能确切知道这一点,那么就可以节省大量的 diff 算法时间。因此, React 允许用户通过 shouldComponentUpdate()来判断该组件是否需要大量 diff 算法分析。
3. element diff
当节点处于同一层级时,通过唯一ID区分。diff 提供三种节点操作:
- `INSERT_MARKUP`(插入):如果新的组件类型不在旧集合里,即全新的节点,需要对新节点执行插入操作。
- `MOVE_EXISTING` (移动):旧集合中有新组件类型,且 element 是可更新的类型,generatorComponentChildren 已调用 receiveComponent,这种情况下 prevChild=nextChild,就需要做移动操作,可以复用以前的 DOM 节点。
- `REMOVE_NODE` (删除):旧组件类型,在新集合里也有,但对应的 elememt 不同则不能直接复用和更新,需要执行删除操作,或者旧组件不在新集合里的,也需要执行删除操作。