DOM Diff 阅读笔记
DOM Diff 算法原理的阅读笔记
https://github.com/ascoders/weekly/blob/master/前沿技术/190.精读《DOM diff 原理详解》.md
1、明确 Dom diff的目的
- 是为了找到如何从 old 变成 new 的操作路径,而不是有什么变化。
- 操作路径不一定是最小路径,因为是算法算的,也要考虑找到最小路线的成本
- 在 jquery 时代,等于是人工找到最小修改路径,但是认为这个跟业务逻辑其实不耦合,实际操作起来,对业务代码来说,心智成本过高
2、Vue Dom diff
- 按层比对
- 主要就是双指针两头收
- 中间部分先创建一个 map
- 然后在新的部分比对 map 的变化,是改变位置,还是删除
- 把添加和删除的动作做完,最后做移动的动作
- 最后按照最长递增子序列的方式,来找最优的改变位置的序列
3、React Dom diff
- 同样是按层比对的,按层比对其实是基于浏览器 DOM 树本身的设计,如果变化的剧烈,按层比对都不是更优的解法,那显然已经都跟切换成一个新网站差不多了。我们默认基于的假设是,dom diff 的幅度时同一块区域,和小范围的
- 仅右移策略:对元素发生的位置变化,只会将其移动到右边,如果右边移动完了,其他位置也就有序了
- 遍历 old 储存 map
- 最后移动位置,按照只向右移动来做。
4.AI的补充:
当然,我可以给你一个具体的例子来说明这个假设。
假设我们有一个列表组件,用于显示一个水果列表。初始状态下,水果列表如下:
jsx
<ul> <li _key_``="apple">苹果</li> <li _key_``="banana">香蕉</li> <li _key_``="orange">橙子</li></ul>
现在,我们要更新这个水果列表,将"香蕉"移动到"橙子"的后面,同时添加一个新的水果"西瓜":
jsx
<ul> <li _key_``="apple">苹果</li> <li _key_``="orange">橙子</li> <li _key_``="banana">香蕉</li> <li _key_``="watermelon">西瓜</li></ul>
按照 React 的 DOM Diff 算法中的"仅向右移动"策略,更新过程如下:
-
React 为新的子节点列表建立 map:
txt
{ "apple": <li key="apple">苹果</li>, "orange": <li key="orange">橙子</li>, "banana": <li key="banana">香蕉</li>, "watermelon": <li key="watermelon">西瓜</li>} -
React 遍历旧的子节点列表:
- 对于
<li key="apple">苹果</li>,在新的 map 中找到了对应的 key,因此 React 会保留该节点。 - 对于
<li key="banana">香蕉</li>,在新的 map 中找到了对应的 key,但是位置比之前更靠右,因此 React 会将该节点向右移动到正确的位置。 - 对于
<li key="orange">橙子</li>,在新的 map 中找到了对应的 key,因此 React 会保留该节点。
- 对于
-
对于新的子节点列表中剩余的节点
<li key="watermelon">西瓜</li>,React 会创建一个新的节点并插入到 DOM 中。
最终更新后的 DOM 结构如下:
jsx
<ul> <li _key_``="apple">苹果</li> <li _key_``="orange">橙子</li> <li _key_``="banana">香蕉</li> <li _key_``="watermelon">西瓜</li></ul>
在这个例子中,"仅向右移动"的策略可以减少 DOM 操作的次数。React 只需要移动"香蕉"节点,并创建一个新的"西瓜"节点,而不需要对其他节点进行多余的操作。
这个例子体现了假设的合理性:在实际应用中,列表的更新通常是局部的,新的子节点列表与旧的子节点列表的顺序相似。"仅向右移动"的策略可以很好地处理这种情况,提高性能。
当然,如果列表的顺序发生了大规模的变化,例如完全反转,"仅向右移动"的策略可能会导致不必要的 DOM 操作。但是在大多数实际应用中,这种情况较为少见。
- 原来精读的总结也非常精确,我这里直接拷贝
- 完全对比 O(n³) 无法接受,故降级为同层对比的 O(n) 方案。
- 为什么降级可行?因为跨层级很少发生,可以忽略。
- 同层级也不简单,难点是如何高效位移,即最小步数完成位移。
- Vue 为了尽量不移动,先左右夹击跳过不变的,再找到最长连续子串保持不动,移动其他元素。
- React 采用仅右移方案,在大部分从左往右移的业务场景中,得到了较好的性能。