Skip to content

实现Diff算法

课前须知: 当前仅实现了单一节点的「增/删操作」,即「单节点Diff算法」。本节课实现「多节点的 Diff算法」。 本节课采用简写示例:A1 -> A2

对于reconcileSingleElement的改动

当前支持的情况: A1 -> B1 A1 -> A2 需要支持的情况: ABC -> A 「单/多节点」是指「更新后是单/多节点」。 更细致的,我们需要区分4种情况: key 相同,type 相同 == 复用当前节点 例如:A1 B2 C3 -> A1 key 相同,type 不同 == 不存在任何复用的可能性 例如:A1 B2 C3 -> B1 key 不同,type 相同  == 当前节点不能复用 key 不同,type 不同 == 当前节点不能复用

对于reconcileSingleTextNode的改动

类似reconcileSingleElement 。

对于同级多节点Diff的支持

单节点需要支持的情况:

插入 Placement
删除 ChildDeletion

多节点需要支持的情况:

插入 Placement
删除 ChildDeletion
移动 Placement

整体流程分为4步。

  1. current 中所有同级fiber 保存在Map
  2. 遍历newChild 数组,对于每个遍历到的element ,存在两种情况: 在Map 中存在对应current fiber ,且可以复用 在Map 中不存在对应current fiber ,或不能复用
  3. 判断是插入还是移动
  4. 最后Map 中剩下的都标记删除

步骤2 —— 是否复用 详解

首先,根据keyMap 中获取current fiber ,如果不存在current fiber ,则没有复 用的可能。 接下来,分情况讨论:

element 是HostText ,current fiber 是么?
element 是其他ReactElement ,current fiber 是么?

TODO element 是数组或Fragmentcurrent fiber 是么?

jsx
<ul>

<li/>

<li/>

{[<li/>, <li/>]}

</ul>
<ul>

<li/>

<li/>

<>

<li/>

<li/>

</>

</ul>

步骤3 —— 插入/移动判断 详解

「移动」具体是指「向右移动」 移动的判断依据:elementindex 与「element对应current fiber」的index 的比较 A1 B2 C3 -> B2 C3 A1 0__1__2______0__1__2 当遍历elemen t时,「当前遍历到的element」一定是「所有已遍历的element」中最靠右 那个。 所以只需要记录「最后一个可复用fiber」在current 中的indexlastPlacedIndex ),在接下来的遍历中: 如果接下来遍历到的「可复用fiber」的index < lastPlacedIndex ,则标记

Placement

否则,不标记 移动操作的执行 Placement 同时对应: 移动 插入 对于插入操作,之前对应的DOM 方法是parentNode.appendChild ,现在为了实现移动操 作,需要支持parentNode.insertBeforeparentNode.insertBefore 需要找到「目标兄弟Host节点」,要考虑2个因素: 可能并不是目标fiber的直接兄弟节点 //情况1

jsx
<A/><B/>

function B() {

return <div/>;

}

//情况2

jsx
<App/><div/>

function App() {

return <A/>;

}

不稳定的Host 节点不能作为「目标兄弟Host节点」 不足 不支持数组与Fragment

jsx
<>
<div/>

<div/>
</>
<ul>

<li/>

<li/>

{[<li/>, <li/>]}

</ul>

可能有未考虑到的边界情况 需要覆盖所有相关用例ReactMultiChildReconcile-test.js

用心学习,用代码说话 💻