上一篇《知根知底setState》介绍了在不一样场景调用setState
会有不一样的处理过程,但无论怎样最终目的都是要进行react的更新,本文咱们就来了解setState以后的更新过程。
提示:源码基于react v16.4.3-alpha.0react
字面上意思能够理解为查找拥有最高优先级的root,这个root又是什么呢?git
// react 项目的入口文件,初始化
ReactDom.render(
<App />,
document.getElementById('app')
)
复制代码
root就是经过react生成的节点树的根节点 <div id="app"></div>
做用:
1,找到拥有最高优先级的root
2,从更新任务链表中删除没有更新任务的root。这个没有更新任务的root的怎么理解呢——本来这个root的其中某个子节点触发了更新,可是更新已经被处理完了,因此root.expirationTime === NoWork
将已处理完成的去除掉github
在findHighestPriorityRoot函数中能够发现 firstScheduledRoot
,lastScheduledRoot
表示一个任务调度链表(环形)的头结点和尾节点,那这个链表在哪里生成的呢——addRootToSchedule源码bash
总结前两个函数的功能:app
addRootToSchedule的调用位置就是在requestWork源码
看过《知根知底setState》的同窗能够知道在将setState的时候到这里就结束了,为何呢?由于接下来就是react fiber的Reconciler和Commit阶段。异步
下面进入本文的主题了react fiber,从requestWork开始函数
源码oop
function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
addRootToSchedule(root, expirationTime);
...
if (expirationTime === Sync) {
// 同步更新
performSyncWork();
} else {
// 异步更新
scheduleCallbackWithExpirationTime(root, expirationTime);
}
}
复制代码
根据expirationTime判断采用同步更新仍是异步更新,本文主要从同步更新的过程来了解fiberpost
// function performSyncWork() {
performWork(Sync, null);
}
复制代码
performSyncWork仅仅只是调用performWork,注意第二个参数为nullui
function performWork(minExpirationTime: ExpirationTime, dl: Deadline | null) {
deadline = dl;
// 保证每一次进行正在处理的更新任务都是优先级最高的
findHighestPriorityRoot()
if (deadline !== null) {
// 处理同步更新
// 在当前找到的优先级最高的root上开始后续的更新工做
performWorkOnRoot(
nextFlushedRoot,
nextFlushedExpirationTime,
currentRendererTime >= nextFlushedExpirationTime,
);
} else {
// 处理异步更
}
}
复制代码
从代码中的if/else
能够发现虽然在requestWork
中同步和异步走向两个不一样的分支,但最后又会合并到同一个处理过程performWork
准备好小板凳和瓜子终于到了fiber的正头戏了,源码
function performWorkOnRoot(
root: FiberRoot,
expirationTime: ExpirationTime,
isExpired: boolean,
) {
// 一样有两个分支——同步和异步
if (deadline === null || isExpired) {
// 同步的处理逻辑
// 咱们知道fiber的Reconciler阶段到Commit阶段是能够被中断的
// finishedWork就是Reconciler阶段diff出来的反作用(新state生成的DOM)
let finishedWork = root.finishedWork;
if (finishedWork !== null) {
// 不为空说明以前有任务被中断,须要如今处理完被中断的任务
completeRoot(root, finishedWork, expirationTime);
} else {
// 没有被中断的任务,就处理当前找到的优先级最高的更新任务
root.finishedWork = null;
// 是否能够被中断,对于同步和异步到期work不可被中断
const isYieldy = false;
// Reconciler阶段
renderRoot(root, isYieldy, isExpired);
// finishedWork其实就是rootWorkInProgress(新的VNode)
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// diff以后获得的结果是:有更新,
// 进入到Commit阶段
completeRoot(root, finishedWork, expirationTime);
}
}
} else {
// 异步的处理逻辑
}
}
复制代码
流程图:
function renderRoot(
root: FiberRoot,
isYieldy: boolean,
isExpired: boolean,
): void {
...
if (
expirationTime !== nextRenderExpirationTime ||
root !== nextRoot ||
nextUnitOfWork === null
) {
// 检查咱们是否从一个新堆栈开始,或者咱们是不是从以前的工做中恢复
...
}
do {
try {
workLoop(isYieldy);
} catch (thrownValue) {
...
}
// 执行一次就跳出循环
break;
} while (true);
}
复制代码
其实从执行setState
到workLoop
若是你能理解,fiber的大概过程你也就理解了。workLoop以后的主要逻辑是进行diff——调用render前生命周期函数,应用新的state执行render获得新的DOM,将要更新的信息挂在到rootWorkInProgress,并最后赋值给finishedWork。
这个过程的代码量比较大,react大多数功能都是在这个阶段进行的(如:ref,context等),固然这部分代码逻辑比较直白式的理解起来也相对简单,若是有兴趣能够自行研究下
function completeRoot(
root: FiberRoot,
finishedWork: Fiber,
expirationTime: ExpirationTime,
): void {
...
commitRoot(root, finishedWork);
}
复制代码
function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
...
// 执行render以后的生命周期
// 应用新state生成的Element(在diff阶段经过createElement等建立)到DOM树种
}
复制代码
这个阶段的逻辑起来也比较简单,对这块感兴趣的同窗能够自行研究下
结合《知根知底setState》和本文咱们粗略介绍了react v16的更新过程。
你可能会发现本文对一些实现细节没有作过多的描述
这是由于对于fiber实现细节介绍的文章网上已经能够搜索出不少,并且讲的也比较详细。
本文主要在同步更新的过程,异步更新和同步更新大多处理逻辑都是通用的,简单说就以不一样的方式进入到更新过程,并且react v16的源码各版本依旧在变更。
我写这篇的目的:
对于想去阅读源码的人为他们提供一个逻辑流程,个中细节他们能够本身慢慢研究(固然这也不是经过一篇两篇文章能描述完整的)
对于只是想了解下内部原理的人省去了理解大量细节处理的烦恼