Reconclier
调合分为两个步骤,递与归,在递阶段首次挂载时会创建整个应用的fiber链表树,在更新时会对比current
和workInprogress
指针指向的两棵树,在更新时就包括diff。 在归阶段为fiberNode打上对应的tag,在commit阶段会被执行本次需要的操作。
beginWork
主要是创建fiber链表。
根据current
指针是否为空来判断时首次挂载还是更新
在首次挂载时会从根节点开始,向下进行深度优先遍历,为遍历到的每一个jsx对象创建对象的fiber链表
在每次遍历到叶子节点child
属性为空时会执行completeWork
, 对新建的fiber节点处理
在满足didReceiveUpdate = false
时, 表示会复用该fiber节点,也就是cloneFiber
begiinWork的函数的目的就是创建fiber链表,并返回下一个工作的子jsx对象。
completeWork
该阶段的主要工作就是创建dom节点, 设置属性, 并在首次挂载每次执行的时候把已经存在的子dom节点加入到dom节点下。创建的dom实例保存在stateNode属性上
Diff
单节点diff
- key不同key不同,将该fiber标记为删除,并重新创建一个新的fiber node
- key相同 ,type不同,将该fiber及其兄弟fiber标记为删除
-
多节点diff
第一轮遍历
遍历newChildren,将newChildren[i]与oldFiber比较,判断DOM节点是否可复用。如果可复用,i++,继续比较newChildren[i]与oldFiber.sibling,可以复用则继续遍历。
- 如果不可复用,分两种情况:
- key不同导致不可复用,立即跳出整个遍历,第一轮遍历结束。
- key相同type不同导致不可复用,会将oldFiber标记为DELETION,并继续遍历
如果newChildren遍历完(即i === newChildren.length - 1)或者oldFiber遍历完(即oldFiber.sibling === null),跳出遍历,第一轮遍历结束。
第二轮遍历
newChildren与oldFiber同时遍历完此时diff结束、
- newCHilren没有遍历完成,oldFiber完了,表示新增的打上
Placement
标记newChildren遍历完成 - oldFiber没有,表示删除打上
Deletion
标记 - 二者都没遍历完成,表示移动,终止本次循环
第三轮遍历
用变量lastPlacedIndex表示最后一个可复用的节点在oldFiber中的位置索引
如果oldIndex < lastPlacedIndex,代表本次更新该节点需要向右移动。
lastPlacedIndex初始为0,每遍历一个可复用的节点,如果oldIndex >= lastPlacedIndex,则lastPlacedIndex = oldIndex。commit阶段
将变化更新到页面的过程before mutation
保存优先级,以同步的优先级,执行完毕后再恢复到之前的优先级
处理focus相关commitBeforeMutationEffects
来处理effectList
commitBeforeMutationEffects
- 处理DOM节点渲染/删除后的焦点失去和获取
- 调用
getSnapshotBeforeUpdate
生命周期函数 -
mutation
遍历
effectList
, 根据effectTag执行增删改。 在这里执行的是commitMutationEffects
commitMutationEffects
重置文本节点
- 更新ref对象
- 根据
effectTag
处理插入、更新、删除、插入并更新等dom操作 - 更新时调用
useLayoutEffect
的销毁函数layout
调用commitLayoutEffects
函数,遍历执行effectList
调用变更之后的同步回调函数,比如setState的第二个参数,useLayoutEffect
、didMount
赋值ref对象
- 对于类组件,根据
current === null
来区分是挂载还是更新调用didMount、didUpdate
. 对于setState的第二个参数,也是在此时调用 - 函数组件,调用
layoutEffect
的回调函数