React render阶段是并发的,首先创建一个宏任务用于构建Fiber树,待浏览器执行渲染等高优操作后,取出宏任务执行,如果5ms 未执行完任务,再次创建一个宏任务,等待下次调度,以此类推直至构建完Fiber 树。也就是构建Fiber树和浏览器绘制渲染是交替进行的。
代码详情
- main.jsx
import * as React from "./react";
import { createRoot } from "react-dom/client";
function FunctionComponent() {
console.log("FunctionComponent");
const [number, setNumber] = React.useState(0);
React.useEffect(() => {
setNumber((number) => number + 1);
}, []);
return (
<div>
<button>{number}</button>
<div style={{ paddingTop: 100 }}>{number}</div>
<div style={{ paddingTop: 100 }}>{number}</div>
<div style={{ paddingTop: 100 }}>{number}</div>
<div style={{ paddingTop: 100 }}>{number}</div>
<div style={{ paddingTop: 100 }}>{number}</div>
<div style={{ paddingTop: 100 }}>{number}</div>
</div>
);
}
let element = <FunctionComponent />;
const root = createRoot(document.getElementById("root"));
root.render(element);
- ReactFiberWorkLoop.js
let workInProgress = null;
let workInProgressRoot = null;
let rootDoesHavePassiveEffect = false;
let rootWithPendingPassiveEffects = null;
let workInProgressRootRenderLanes = NoLanes;
const RootInProgress = 0;
const RootCompleted = 5;
let workInProgressRootExitStatus = RootInProgress;
export function scheduleUpdateOnFiber(root, fiber, lane) {
markRootUpdated(root, lane);
ensureRootIsScheduled(root);
}
function ensureRootIsScheduled(root) {
const nextLanes = getNextLanes(root, NoLanes);
if (nextLanes === NoLanes) {
return;
}
let newCallbackPriority = getHighestPriorityLane(nextLanes);
let newCallbackNode;
if (newCallbackPriority === SyncLane) {
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
queueMicrotask(flushSyncCallbacks);
newCallbackNode = null;
} else {
let schedulerPriorityLevel;
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediateSchedulerPriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdleSchedulerPriority;
break;
default:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}
newCallbackNode = Scheduler_scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
root.callbackNode = newCallbackNode;
}
function performSyncWorkOnRoot(root) {
const lanes = getNextLanes(root);
renderRootSync(root, lanes);
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
commitRoot(root);
return null;
}
function performConcurrentWorkOnRoot(root, didTimeout) {
console.log("performConcurrentWorkOnRoot");
const originalCallbackNode = root.callbackNode;
const lanes = getNextLanes(root, NoLanes);
if (lanes === NoLanes) {
return null;
}
const shouldTimeSlice = !includesBlockingLane(root, lanes) && !didTimeout;
console.log("shouldTimeSlice", shouldTimeSlice);
const exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
if (exitStatus !== RootInProgress) {
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
commitRoot(root);
}
if (root.callbackNode === originalCallbackNode) {
return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
}
function renderRootConcurrent(root, lanes) {
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
prepareFreshStack(root, lanes);
}
workLoopConcurrent();
if (workInProgress !== null) {
return RootInProgress;
}
return workInProgressRootExitStatus;
}
function commitRoot(root) {
const previousUpdatePriority = getCurrentUpdatePriority();
try {
setCurrentUpdatePriority(DiscreteEventPriority);
commitRootImpl(root);
} finally {
setCurrentUpdatePriority(previousUpdatePriority);
}
}
function commitRootImpl(root) {
const { finishedWork } = root;
workInProgressRoot = null;
workInProgressRootRenderLanes = null;
root.callbackNode = null;
if (
(finishedWork.subtreeFlags & Passive) !== NoFlags ||
(finishedWork.flags & Passive) !== NoFlags
) {
if (!rootDoesHavePassiveEffect) {
rootDoesHavePassiveEffect = true;
Scheduler_scheduleCallback(NormalSchedulerPriority, flushPassiveEffect);
}
}
const subtreeHasEffects =
(finishedWork.subtreeFlags & MutationMask) !== NoFlags;
const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags;
if (subtreeHasEffects || rootHasEffect) {
commitMutationEffectsOnFiber(finishedWork, root);
commitLayoutEffects(finishedWork, root);
if (rootDoesHavePassiveEffect) {
rootDoesHavePassiveEffect = false;
rootWithPendingPassiveEffects = root;
}
}
root.current = finishedWork;
}
function prepareFreshStack(root, renderLanes) {
workInProgress = createWorkInProgress(root.current, null);
workInProgressRootRenderLanes = renderLanes;
workInProgressRoot = root;
finishQueueingConcurrentUpdates();
}
function renderRootSync(root, renderLanes) {
if (
root !== workInProgressRoot ||
workInProgressRootRenderLanes !== renderLanes
) {
prepareFreshStack(root, renderLanes);
}
workLoopSync();
}
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
console.log("shouldYield()", shouldYield(), workInProgress);
sleep(600);
performUnitOfWork(workInProgress);
}
}
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
const next = beginWork(current, unitOfWork, workInProgressRootRenderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
}
function completeUnitOfWork(unitOfWork) {
let completedWork = unitOfWork;
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
completeWork(current, completedWork);
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
}
completedWork = returnFiber;
workInProgress = completedWork;
} while (completedWork !== null);
if (workInProgressRootExitStatus === RootInProgress) {
workInProgressRootExitStatus = RootCompleted;
}
}
export function requestUpdateLane() {
const updateLane = getCurrentUpdatePriority();
if (updateLane !== NoLanes) {
return updateLane;
}
const eventLane = getCurrentEventPriority();
return eventLane;
}
function sleep(duration) {
const timeStamp = new Date().getTime();
const endTime = timeStamp + duration;
while (true) {
if (new Date().getTime() > endTime) {
return;
}
}
}
- Scheduler.js
function getCurrentTime() {
return performance.now();
}
var maxSigned31BitInt = 1073741823;
var IMMEDIATE_PRIORITY_TIMEOUT = -1;
var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
var NORMAL_PRIORITY_TIMEOUT = 5000;
var LOW_PRIORITY_TIMEOUT = 10000;
var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;
let taskIdCounter = 1;
const taskQueue = [];
let scheduleHostCallback = null;
let startTime = -1;
let currentTask = null;
const frameInterval = 5;
const channel = new MessageChannel();
var port2 = channel.port2;
var port1 = channel.port1;
port1.onmessage = performWorkUntilDeadline;
function scheduleCallback(priorityLevel, callback) {
const currentTime = getCurrentTime();
const startTime = currentTime;
let timeout;
switch (priorityLevel) {
case ImmediatePriority:
timeout = IMMEDIATE_PRIORITY_TIMEOUT;
break;
case UserBlockingPriority:
timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
break;
case IdlePriority:
timeout = IDLE_PRIORITY_TIMEOUT;
break;
case LowPriority:
timeout = LOW_PRIORITY_TIMEOUT;
break;
case NormalPriority:
default:
timeout = NORMAL_PRIORITY_TIMEOUT;
break;
}
const expirationTime = startTime + timeout;
const newTask = {
id: taskIdCounter++,
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: expirationTime,
};
push(taskQueue, newTask);
requestHostCallback(workLoop);
return newTask;
}
function shouldYieldToHost() {
const timeElapsed = getCurrentTime() - startTime;
if (timeElapsed < frameInterval) {
return false;
}
return true;
}
function workLoop(startTime) {
let currentTime = startTime;
currentTask = peek(taskQueue);
while (currentTask !== null) {
if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
break;
}
const callback = currentTask.callback;
if (typeof callback === "function") {
currentTask.callback = null;
const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
const continuationCallback = callback(didUserCallbackTimeout);
if (typeof continuationCallback === "function") {
currentTask.callback = continuationCallback;
return true;
}
if (currentTask === peek(taskQueue)) {
pop(taskQueue);
}
} else {
pop(taskQueue);
}
currentTask = peek(taskQueue);
}
if (currentTask !== null) {
return true;
}
return false;
}
function requestHostCallback(workLoop) {
scheduleHostCallback = workLoop;
schedulePerformWorkUntilDeadline();
}
function schedulePerformWorkUntilDeadline() {
port2.postMessage(null);
}
function performWorkUntilDeadline() {
if (scheduleHostCallback) {
startTime = getCurrentTime();
let hasMoreWork = true;
try {
hasMoreWork = scheduleHostCallback(startTime);
} finally {
if (hasMoreWork) {
schedulePerformWorkUntilDeadline();
} else {
scheduleHostCallback = null;
}
}
}
}
- 开启并发模式
export const allowConcurrentByDefault = true;

并发渲染.png