主题
实现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步。
- 将
current中所有同级fiber保存在Map中 - 遍历
newChild数组,对于每个遍历到的element,存在两种情况: 在Map中存在对应current fiber,且可以复用 在Map中不存在对应current fiber,或不能复用 - 判断是插入还是移动
- 最后
Map中剩下的都标记删除
步骤2 —— 是否复用 详解
首先,根据key 从Map 中获取current fiber ,如果不存在current fiber ,则没有复 用的可能。 接下来,分情况讨论:
element 是HostText ,current fiber 是么?
element 是其他ReactElement ,current fiber 是么?TODO element 是数组或Fragment ,current fiber 是么?
jsx
<ul>
<li/>
<li/>
{[<li/>, <li/>]}
</ul>
<ul>
<li/>
<li/>
<>
<li/>
<li/>
</>
</ul>步骤3 —— 插入/移动判断 详解
「移动」具体是指「向右移动」 移动的判断依据:element 的index 与「element对应current fiber」的index 的比较 A1 B2 C3 -> B2 C3 A1 0__1__2______0__1__2 当遍历elemen t时,「当前遍历到的element」一定是「所有已遍历的element」中最靠右 那个。 所以只需要记录「最后一个可复用fiber」在current 中的index (lastPlacedIndex ),在接下来的遍历中: 如果接下来遍历到的「可复用fiber」的index < lastPlacedIndex ,则标记
Placement否则,不标记 移动操作的执行 Placement 同时对应: 移动 插入 对于插入操作,之前对应的DOM 方法是parentNode.appendChild ,现在为了实现移动操 作,需要支持parentNode.insertBefore 。 parentNode.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