workLoop是react中一个非常重要的执行函数,今天的这篇文章我们需要完成下面几个小目标:
- 理解什么是workLoop?
- 熟悉workLoop的流程是什么?
- 掌握workLoop源码是如何实现的?
在开始今天的文章前,我们先从上文中关键的一句话入手。
react把原先一个完整的diff任务拆成颗粒度更细的任务
这里提到的颗粒度更细的任务,任务其实指的是构建fiber数据结构这样的事情,所以上文可以翻译成react把原先一个完整的diff工作拆成了很多构建fiber数据结构的任务。那我们需要看看一个fiber,他的数据结构到底长什么样子?
源码位置------fiber数据结构
class FiberNode {
return: FiberNode | null;
child: FiberNode | null;
sibling: FiberNode | null;
index: number;
pendingProps: Props;
memorizedProps: Props;
memorizedState: Props;
updateQueue: any;
key: Key;
tag: workTags;
type: any;
stateNode: any;
ref: Ref;
alternate: FiberNode | null;
flags: Flags;
subTreeFlags: Flags;
deletions: FiberNode[] | null;
constructor(tag: workTags, pendingProps: Props, key: Key) {
// 描述fiber节点关系
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
// 描述状态
this.pendingProps = pendingProps;
this.memorizedProps = null;
this.memorizedState = null;
this.updateQueue = null;
// 描述dom
this.tag = tag;
this.type = null;
this.stateNode = null;
this.key = key || null;
this.ref = null;
this.alternate = null;
this.flags = Noflags;
this.subTreeFlags = Noflags;
this.deletions = null;
}
}
所以他其实就是个对象,里面包含了各个fiber之间的关系,状态、dom信息、副作用等等。那么多个fiber就是通过上面retun、child、sibling连接起来的,构成了一颗fiber树。
如上图,一个个fiber相互联系了起来,它和以往熟知的virtualDom树不同,它是个链表结构,为什么要做成这样的数据结构?它是通过什么构建成这样的结构?这些我们留到后续的文章中详细讲解,目前只需要明白,一个个fiber的构建是react内部的任务。
ok 我们转到今天的主角------workLoop
老规矩 先说结论: 理念上它类比于事件循环,如果队列中存在任务,则一直从中取出任务去执行,而这里的任务想必大家已经知道是什么了吧,它就是上面提到的一个个fiber单元的构建。so, workLoop干的就是这个事情。
这就是第一个小目标的答案,在react源码中我也标注出来 workLoop
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
如上,它开启的是个while循环,不断的去判断当前的workInProgress是否存在,若存在则继续执行构建的任务。
那么具体performUnitOfWork做了什么事情源码位置
源码简化来看其实就是如下:
const performUnitOfWork = (fiber: FiberNode) => {
// 先深度优先遍历 模拟递的过程
const next = beginWork(fiber, wipRootRenderLane);
fiber.memorizedProps = fiber.pendingProps;
if (next === null) {
// 没有子fiber
do {
completeWork(fiber);
const sibling = fiber.sibling;
if (sibling) {
return (workInProgress = sibling);
}
fiber = fiber.return;
workInProgress = fiber;
} while (fiber !== null);
} else {
workInProgress = next;
}
};
我们在这篇文章中,不会深究具体某个函数内部做了什么事情,我们关注点放在workLoop是如何把任务进行串联起来的。所以抽象看起来,发现内部其实执行了2个函数,一个是beginWork、completeWork,他们会去修改workInProgress的值,由于workInProgress被修改了从而导致不断的驱动workLoop进行while循环
workInProgress其实是个全局的变量,标记当前运行到哪个fiber单元了。
整个构建调度的过程,其实和递归逻辑一样,都是深度优先遍历。 beginWork和completeWork会交替执行,下面是他们运行过程的文字表述:
- 先遍历到叶子节点(beginWork做的事,向下走),next === null代表目前是叶子节点,没有子节点next了,这一阶段的beginWork结束。
- 进入到completeWork函数,完成一系列操作(后续详解),它执行过程会判断是否有兄弟节点存在(fiber.sibling === true),如果存在那么直接打断,修改兄弟节点为workInProgress,这样兄弟节点会进行beginWork阶段,走第一步的操作。
- 若兄弟节点不存在,就会走向父级节点(fiber=fiber.return;workInProgress = fiber),再走到判断兄弟节点是否存在的逻辑,一直向上追溯到顶级节点,直到null,构建结束。
相信光看文字表述还是不太好理解,所以配置了流程图,大家可以参照流程图与上述的表达对应起来,就能清晰的明白workLoop是如何调度fiber工作的构建的。
ok,以上就是文章的所有内容了,我们总结下:
- react中的任务被拆分成一个个fiber单元构建。
- fiber数据结构是链表,记录了各个节点关系,dom信息、状态等等。
- workLoop就是维护第一点不断构建的调度逻辑。
- workLoop本身是用遍历模拟递归的方式实现,内部主要是2个函数,beginWork(类似递归中的递)、completeWork(类似递归中的归),整个过程是交替执行这2个函数,直到整棵fiber结构构建完成。
同样,了解了这些内容,相信会带来更多的疑问,譬如:
- 开发者写的代码是如何唤起workLoop执行的?
- 整棵fiber结构又是如何转换成页面?
- beginWork内部具体做了什么?
- complete内部又做了什么?
- 。。。。
so,让我们带着问题去期待下一篇文章~