链表对比递归
因为fiber架构要用每一帧的空闲时间执行任务,那么任务列表是会被中断的,
如果使用递归的话,查找当前需要执行的任务时,每次都要从根节点开始遍历,效率反而会降低。
使用链表的话,有个 Fiber 其单链表(Linked List)结构为 A → B → C,当 A 执行到 B 被中断的话,可以之后再次执行 B → C,时间复杂度是O(1)。
fiber链表单元结构
Fiber 可以理解为是一种数据结构,React Fiber 就是采用链表实现的。每个 Virtual DOM 都可以表示为一个 fiber,
// 部分结构,其实就是一个对象数据结构
{
alternate: Fiber|null, // 在fiber更新时克隆出的镜像fiber,对fiber的修改会标记在这个fiber上(实际上是两颗fiber数,用于更新缓存,提升运行效率)
nextEffect: Fiber | null, // 单链表结构,方便遍历 Fiber Tree 上有副作用的节点
pendingWorkPriority: PriorityLevel, // 标记子树上待更新任务的优先级
stateNode: any, // 管理 instance 自身的特性
return: Fiber|null, // 指向 Fiber Tree 中的父节点
child: Fiber|null, // 指向第一个子节点
sibling: Fiber|null, // 指向兄弟节点
}
fiber单元之间的关联关系组成fiber tree,fiber tree是根据 VDOM tree 构造出来的,树形结构完全一致,只是包含的信息不同
fiber树
根据以下的dom结构会生成一个相应的fiber树(如图)
let root = {
key: 'A1',
children: [
{
key: 'B1',
children: [
{ key: 'C1', children: [] },
{ key: 'C2', children: [] },
]
},
{
key: "B2", children: []
}
]
}
image.png
fiber的执行顺序的原则是
儿子 ===》兄弟 ===》叔叔
image.pngfiber render阶段链表执行顺序实现
根据以上dom树生成的简易fiber树(实现方式在下一节说明)
let A1 = { type: 'div', key: 'A1'};
let B1 = { type: 'div', key: 'B1', return: A1 }
let B2 = { type: 'div', key: 'B2', return: A1 }
let C1 = { type: 'div', key: 'C1', return: B1 }
let C2 = { type: 'div', key: 'C2', return: B1 }
A1.child = B1;
B1.sibling = B2;
B1.child = C1;
C1.sibling = C2;
fiber单元执行顺序
let nextUnitOfWork = null;//下一个执行单元
let startTime = Date.now();
function workLoop(deadline) {
//while (nextUnitOfWork) {//如果有待执行的执行单元,就执行,然后会返回下一个执行单元
while ((deadline.timeRemaining() > 1 || deadline.didTimeout) && nextUnitOfWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
if (!nextUnitOfWork) {
console.log('render阶段结束了');
} else {//请求下次浏览器空闲的时候帮我调
requestIdleCallback(workLoop, { timeout: 1000 });
}
}
function performUnitOfWork(fiber) {
beginWork(fiber);//处理此fiber
if (fiber.child) {//如果有儿子,返回大儿子
return fiber.child;
}//如果没有儿子,说明此fiber已经完成了
while (fiber) {
completeUnitOfWork(fiber);
if (fiber.sibling) {
return fiber.sibling;//如果说有弟弟返回弟弟
}
fiber = fiber.return;// 如果没有弟弟当前的fiber指向自己的父亲,父亲也完成了,继续循环返回父亲的弟弟
}
}
function completeUnitOfWork(fiber) {
console.log('结束', fiber.key);// C1 C2 B1 B2 A1
}
function beginWork(fiber) {
sleep(20);
console.log('开始', fiber.key);//A1 B1 C1 C2 B2
}
nextUnitOfWork = A1;
requestIdleCallback(workLoop, { timeout: 1000 });
image.png
网友评论