React17中的DOM-DIFF算法
React17中的dom-diff是老fiber树跟新jsx的对比,生成新的fiber树的过程
- 单节点比较
- 多节点比较
1.单节点比较
如果新的子节点(JSX)只有一个元素的话,老fiber可以是一个或者多个
- 两个节点相同的条件是
type和key都相同 - 插入:
Placement=2 - 更新:
Update=4 - 插入并更新:
PlacementAndUpdate=6 - 删除:
Deletion=8
1.1.一对一,type不同
//老
<div>
<h1>h1</h1>
</div>
//新
<div>
<h2>h1</h2>
</div>
- 1.在调和阶段把这个老fiber标记为删除
- 2.根据新的jsx生成新的fiber节点并标记为插入
- 3.在提交阶段执行删除h1和添加h2操作
1.2.一对一,key不同
//老
<div>
<h1 key='h1'>h1</h1>
</div>
//新
<div>
<h1 key='h2'>h1</h1>
</div>
- 1.在调和阶段把这个老fiber标记为删除
- 2.根据新的jsx生成新的fiber节点并标记为插入
- 3.在提交阶段执行删除h1和添加h2操作
1.3.一对一,type&key相同
//老
<div>
<h1 key='h1'>h1</h1>
</div>
//新
<div>
<h1 key='h1'>h2</h1>
</div>
- 1.在调和阶段把这个老fiber标记为更新,讲新的属性更新到老的fiber上
- 2.在提交阶段进行更新
1.4.一对多
//老
<div>
<h1 key="h1">h1</h1>
<h2 key="h2">h2</h2>
</div>
//新
<div>
<h2 key="h2">h2</h2>
</div>
- 将h1标记为删除
- 将h2标记为更新
- 提交阶段进行删除,更新
2.多节点
- 多节点会经历两轮遍历
- 第一轮遍历主要是处理节点的更新标记,更新包括属性和类型的更新
- 第二轮遍历主要处理节点的新增,删除和移动标记
- 移动是遵循尽量少移动,新的地位高的不动,地位低的动
2.1.一一对比,都可复用
//老
<ul>
<li key="A">A</li>
<li key="B">B</li>
<li key="C">C</li>
<li key="D">D</li>
</ul>
//新
<ul>
<li key="A">A-new</li>
<li key="B">B-new</li>
<li key="C">C-new</li>
<li key="D">D-new</li>
</ul>
- 一一对比,全部标记为更新
2.2.一一对比,key相同,type不同
//老
<ul>
<li key="A">A</li>
<li key="B">B</li>
<li key="C">C</li>
<li key="D">D</li>
</ul>
//新
<ul>
<div key="A">A-new</div>
<li key="B">B-new</li>
<li key="C">C-new</li>
<li key="D">D-new</li>
</ul>
- 第一轮对比,key='A'相同,但是type不同,老fiber标记为删除,根据新的jsx生成新的fiber节点
- 进入第二轮循环,将老的fiber的type和key映射成一个map={B:'li',C:'li',D:'li},进行循环遍历,将剩余的老fiber标记为更新
2.3.多对多,乱序
//老
<ul>
<li key="A">A</li>
<li key="B">B</li>
<li key="C">C</li>
<li key="D">D</li>
<li key="E">E</li>
<li key="F">F</li>
</ul>
//新
<ul>
<li key="A">A-new</li>
<li key="C">C-new</li>
<li key="E">E-new</li>
<li key="B">B-new</li>
<li key="G">G</li>
</ul>
- 第一轮对比,将fiber A 标记为更新
- 进入第二轮循环,将老的fiber的type和key映射成一个map={B:'li',C:'li',D:'li',E:'li',F:'li},进行循环遍历,标记可以复用,删除,移动