1、Update 对象
- eventTime:任务时间,通过
performance.now()获取的毫秒数 - lane:优先级相关字段
- suspenseConfig:
Suspense相关,暂不关注。 - tag:更新的类型,包括
UpdateState|ReplaceState|ForceUpdate|CaptureUpdate。 - payload:更新挂载的数据
- callback:更新的回调函数
- next:与其他
Update连接形成链表
const update: Update<*> = {
eventTime,
lane,
suspenseConfig,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
2、Update 与 Fiber的联系
Fiber节点上的多个Update会组成链表并被包含在fiber.updateQueue中
3、updateQueue
- baseState
- 本次更新前该
Fiber节点的state
- 本次更新前该
firstBaseUpdate与lastBaseUpdate- 由于某些
Update优先级较低,在上次Update计算state时被跳过 - 以链表形式存在,链表头为
firstBaseUpdate,链表尾为lastBaseUpdate
- 由于某些
shared.pending- 本次更新产生的
Update对象 - 保存在
shared.pending中形成单向环状链表 - 当由
Update计算state时这个环会被剪开并连接在lastBaseUpdate后面
- 本次更新产生的
- effects:数组,保存
update.callback !== null的Update。
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
},
effects: null,
};
4、案例:updateQueue的工作流
假设有一个fiber刚经历commit阶段完成渲染。该fiber上有两个由于优先级过低所以在上次的render阶段并没有处理的Update。他们会成为下次更新的baseUpdate。
我们称其为u1和u2,其中u1.next === u2。
fiber.updateQueue.firstBaseUpdate === u1;
fiber.updateQueue.lastBaseUpdate === u2;
u1.next === u2;
// 用`-->`表示链表的指向
fiber.updateQueue.baseUpdate: u1 --> u2
此时fiber上触发两次状态更新,产生两个新的Update,我们称为u3和u4
// 当插入`u3`后:
fiber.updateQueue.shared.pending === u3;
u3.next === u3;
// `shared.pending`的环状链表,用图表示为:
fiber.updateQueue.shared.pending: u3 ─────┐
^ |
└──────┘
// 接着插入`u4`之后:
fiber.updateQueue.shared.pending === u4;
u4.next === u3;
u3.next === u4;
// `shared.pending`是环状链表,用图表示为:
fiber.updateQueue.shared.pending: u4 ──> u3
^ |
└──────┘
// 调度完成进入`render阶段`
// 此时`shared.pending`的环被剪开并连在`updateQueue.lastBaseUpdate`后面
fiber.updateQueue.baseUpdate: u1 --> u2 --> u3 --> u4
// 接下来遍历`updateQueue.baseUpdate`链表,在遍历时如果有优先级低的`Update`会被跳过
5、更新过程中两个疑问
低优先级的任务被打断后如何保证Update不丢失?
-
shared.pending会被同时连接在 workInProgress updateQueue.lastBaseUpdate 与 current updateQueue.lastBaseUpdate 后面 -
当
render阶段被中断后重新开始时,会基于 current updateQueue 克隆出 workInProgress updateQueue。由于 current updateQueue.lastBaseUpdate 已经保存了上一次的 Update,所以不会丢失。 -
当
commit阶段完成渲染,由于 workInProgress updateQueue.lastBaseUpdate 中保存了上一次的 Update,所以 workInProgress Fiber树 变成 current Fiber树 后也不会造成 Update 丢失
如何保证状态依赖的连续性?
优先级:( A1 == C1 ) > ( B2 == D2 )
// 初始状态
baseState: ''
shared.pending: A1 --> B2 --> C1 --> D2
// 第一次 render 调用 A1 --> C1
baseState: ''
baseUpdate: null
memoizedState: 'AC' // 中间值
// 第一次 render 调用 B2 --> C1 --> C2
baseState: 'A' // 此处为'A',并非 'AC'
baseUpdate: B2 --> 'C1' --> D2 // 'C1'会重新被计算一次
memoizedState: 'ABCD' // 最终值