react源码学习
Posted coderlin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react源码学习相关的知识,希望对你有一定的参考价值。
继上一篇react源码学习(1)
创建FiberRoot和rootFiber
//render调用方法
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>, // mount是null
children: ReactNodeList, // ReactElement
container: Container, //容器
forceHydrate: boolean,
callback: ?Function, //回叼
)
const maybeRoot = container._reactRootContainer;
let root: FiberRoot;
if (!maybeRoot)
// Initial mount //初始化mount
root = legacyCreateRootFromDOMContainer(
container,
children,
parentComponent,
callback,
forceHydrate,
);
else
// 第二次mount的时候
root = maybeRoot;
if (typeof callback === 'function')
const originalCallback = callback;
callback = function()
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
;
// Update
updateContainer(children, root, parentComponent, callback);
return getPublicRootInstance(root);
- legacyRenderSubtreeIntoContainer是render最后调用的方法,他会判断当前容器是否有挂载属性,有的话就直接服用老的FiberRoot,然后调用updateContainer方法开启调度,比如wbepack热更新。
- 对于第一次,会调用legacyCreateRootFromDOMContainer方法,创建FiberRoot
function legacyCreateRootFromDOMContainer(
container: Container,
initialChildren: ReactNodeList,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
isHydrationContainer: boolean,
): FiberRoot
if (isHydrationContainer)
else
// First clear any existing content.
let rootSibling;
// 创建FiberRoot
const root = createContainer(
container,
LegacyRoot,
null, // hydrationCallbacks
false, // isStrictMode
false, // concurrentUpdatesByDefaultOverride,
'', // identifierPrefix
noopOnRecoverableError, // onRecoverableError
null, // transitionCallbacks
);
// 容器上挂载root
container._reactRootContainer = root;
// Initial mount should not be batched.
// 初始化渲染优先级比较高,不应该不应分批处理。
flushSync(() =>
// 进入调度阶段了
updateContainer(initialChildren, root, parentComponent, callback);
);
return root;
可以看到这个方法就是创建FiberRoot然后挂载到容器上,最后调用flushSync调用updateContainer调度。flushSync表示以同步到方式执行updateContainer。因为初始化渲染优先级比较高。
那么createContainer就是用来创建FiberRoot和rootFiber的
createConinter会调用createFiberRoot
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
initialChildren: ReactNodeList,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onRecoverableError: null | ((error: mixed) => void),
transitionCallbacks: null | TransitionTracingCallbacks,
): FiberRoot
// FiberRoot
const root: FiberRoot = (new FiberRootNode(
containerInfo,
tag,
hydrate,
identifierPrefix,
onRecoverableError,
): any);
// RootFiber
const uninitializedFiber = createHostRootFiber(
tag,
isStrictMode,
concurrentUpdatesByDefaultOverride,
);
//FiberRoot和rooFiber联系
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
const initialState: RootState =
element: initialChildren,
isDehydrated: hydrate,
cache: (null: any), // not enabled yet
transitions: null,
;
// rootFiber的初始化state
uninitializedFiber.memoizedState = initialState;
// 初始化RootFiber的updateQueue
initializeUpdateQueue(uninitializedFiber);
return root;
createFiberRoot主要做了
- 1 创建FIberRoot和rootFiber并且联系起来
- 2 初始化rootFiber的state
- 3 初始化rootFiber的updateQueue
创建任务对象并存储于任务队列
上面说到updateContainer会开启调度。
它会创建一个update任务,插入到rootFiber上,并且开启调度。
export function updateContainer(
element: ReactNodeList, // <App/>
container: OpaqueRoot, // FiberRoot
parentComponent: ?React$Component<any, any>, // null
callback: ?Function, )
// current就是RootFiber
const current = container.current;
// 获取当前react应用初始化的时间
const eventTime = requestEventTime();
// 获取优先级
const lane = requestUpdateLane(current);
// 创建update
const update = createUpdate(eventTime, lane);
// 对于HostRoot,update的payload就是element
update.payload = element;
// 将update插入fiber.updateQueue上
enqueueUpdate(current, update, lane);
// 开启调度
const root = scheduleUpdateOnFiber(current, lane, eventTime);
if (root !== null)
entangleTransitions(root, current, lane);
return lane;
- 创建update
- 插入到fiber.updateQueue.shard.pending上
- 调用scheduleUpdateOnFiber开启调度
执行任务前的准备工作
上面已经创建了update,放入了任务队列之中,现在应该来执行任务了。updateConitnaer调用了schedlueUpdateOnFiber来开启调度。
这个方法
// 创建Update之后,就需要开启调度更新了。
// 做的事情:
// 1: 通过markUpdateLaneFromFiberToRoot找到rootFiber
// 2: 找到rootFiber之后,调用ensureRootIsScheduled开始调度
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number
)
/**
* 判断是否hi无限循环的update,如果是就报错。
* 比如在componentWillUpdate或者componentDidupdate生命周期中重复调用setState方法,就会发生这种情况。
* react限制了嵌套更新的数量防止无限制更新,限制的嵌套数量是50
*/
checkForNestedUpdates();
// 遍历找到rootFiber
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (root === null)
return null;
// 开始调度
ensureRootIsScheduled(root, eventTime);
主要做了两件事情
- 通过fiber找到rootFiber
- 调用ensureRootIsScheduled开始调度
ensureRootIsScheduled
顾名思义,确保root正在调度。
ensureRootIsScheduled这个方法是关键。他是react自己实现的一个优先级的调度函数。
如
- 判断当前调度任务的优先级以及是否有正在调度的任务,有就判断两者优先级,优先级相同,则不做处理,优先级不同,打断当前的调度,开启新的调度,优先执行优先级高的任务
function ensureRootIsScheduled(root: FiberRoot, currentTime: number)
//正在工作的任务
const existingCallbackNode = root.callbackNode;
//当前调度的任务的优先级
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
// 如果当前调度的任务优先级是NoLanes,不需要调度,直接刷新全局变量,并且取消当前的工作的任务
if (nextLanes === NoLanes)
// Special case: There's nothing to work on.
if (existingCallbackNode !== null)
cancelCallback(existingCallbackNode);
root.callbackNode = null;
root.callbackPriority = NoLane;
return;
// 获取此次任务的Priority
const newCallbackPriority = getHighestPriorityLane(nextLanes);
// 获取当前正在执行的任务的优先级
const existingCallbackPriority = root.callbackPriority;
if ( existingCallbackPriority === newCallbackPriority)
return;
//如果不一样,并且存在正在工作的任务,取消当前正在工作的任务
if (existingCallbackNode != null)
// Cancel the existing callback. We'll schedule a new one below.
cancelCallback(existingCallbackNode);
- 第二则是通过判断当前调度任务的优先级,同步的话调用performSyncWorkOnRoot,异步的话调用performConcurrentWorkOnRoot。
// 调度一个新的任务
let newCallbackNode;
// 判断当前调度的任务是同步还是异步
if (newCallbackPriority === SyncLane)
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
else
// 异步调度,scheduleCallback的返回值就是当前注册的任务newTask
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;
走到这里表示了当前调度的任务是最高优先级的,所以,他会把当前最新的任务重新挂载在root上,注意,这个跟另一个函数performConcurrentWorkOnRoot配合,实现react自己任务优先级的调度。
- 我们现在只需要了解ensureRootIsSchedule主要就是用来判断当前是否有更高优先级的任务,有的话就停止当前任务,创建新的调度
构建workInprogress Fiber树中的rootFiber
当面说到执行performConcurrentWorkOnRoot函数,他是render阶段的入口。
performConcurrentWorkOnRoot会根据当前任务是否过期,决定调用renderRootConcurrent或者renderRootSync,这两个函数会返回一个状态,performConcurrentWorkOnRoot根据这个状态来决定是否进入commit阶段。还是开启新的一轮调度。
function performConcurrentWorkOnRoot(root, didTimeout)
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
因为performConcurretnWorkOnRoot是通过Scheduler调度的,所以她会接受一个didTimeout参数,表示当前帧是否有剩余时间。
重点看下renderRootConcurrent
renderRootConcurrent会创建workInprogress fiber树的rootFiber,
然后调用workLoopConcurrent真正去执行任务。
function renderRootConcurrent(root: FiberRoot, lanes: Lanes)
// 当wokrInprogress不等于root,就要创建workInprogress
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes)
// 构建workInporgressFiber 树及rootFiber
prepareFreshStack(root, lanes);
do
try
// 真正执行任务
workLoopConcurrent();
break;
catch (thrownValue)
handleError(root, thrownValue);
while (true);
prepareFreshStack用于创建workInprogress Fiber的rootFiber。
function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber
root.finishedWork = null; // finishWork标识render阶段完成后构建待提交的对象
root.finishedLanes = NoLanes; //初始化优先级
if (workInProgress !== null)
let interruptedWork = workInProgress.return;
while (interruptedWork !== null)
const current = interruptedWork.alternate;
unwindInterruptedWork(
current,
interruptedWork,
workInProgressRootRenderLanes
);
interruptedWork = interruptedWork.return;
// 构建workInprogress的FiberRoot
workInProgressRoot = root;
// 构建rootFiber
const rootWorkInProgress = createWorkInProgress(root.current, null);
workInProgress = rootWorkInProgress;
workInProgressRootRenderLanes =
subtreeRenderLanes =
workInProgressRootIncludedLanes =
lanes;
workInProgressRootExitStatus = RootInProgress;
workInProgressRootFatalError = null;
workInProgressRootSkippedLanes = NoLanes;
workInProgressRootInterleavedUpdatedLanes = NoLanes;
workInProgressRootRenderPhaseUpdatedLanes = NoLanes;
workInProgressRootPingedLanes = NoLanes;
workInProgressRootConcurrentErrors = null;
workInProgressRootRecoverableErrors = null;
return rootWorkInProgress;
可以看到,调用createWorkInProgress就是来创建workInprogress fiber树的rootFiber。
workLoopConcurrent方法解析
上面说到,workLoopConcurrent是真正执行任务的方法,
function workLoopConcurrent()
// Perform work until Scheduler asks us to yield
while (workInProgress !== null && !shouldYield())
performUnitOfWork(workInProgress);
- 我们刚刚才创建了workInprogress fiber的rootFiber赋值给workInProgress ,shouldYild是判断当前帧是否还有多余时间让他执行performUnitOfWork。
- react16可以中断的最小粒度就是fiber,每一帧至少会执行一个fiebr的调度。当shouldYield返回true的时候,表示该中断了,得把线权交给浏览器了。
- 然后退出循环。renderRootConcurrent会返回一个退出状态给performConcurrentWorkOnRoot,performConcurrentWorkOnRoot会决定要不要进入commit阶段。
- 如果任务还没结束,performConcurrentWorkOnRoot不会进入commit阶段,反而会继续调用ensureRootIsScheduled方法,就是我们前面说的可以来判断当前是否有更高优先级的任务,如果没有,那么performConcurrentWorkOnRoot就会继续返回当前的任务。而schedulerCallback执行的时候会以当前任务的返回值决定该任务是否需要继续调度,需要的话就下一帧继续执行。
performUnitOfWork解析
上面说到。workLoopConcurrent会调用performUnitOfWork去调度fiber。那么performUnitOfWork是怎么处理fiber的呢?
function performUnitOfWork(unitOfWork: Fiber): void
const current = unitOfWork.alternate;
let next;
next = beginWork(current, unitOfWork, subtreeRenderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null)
// 进入归阶段
completeUnitOfWork(unitOfWork);
else
workInProgress = next;
ReactCurrentOwner.current = null;
- 参数就是当前调度的fiber,对于第一次,这就是workInprogress fiber的rootFiber。
- beginWork,递阶段,处理fiber,创建子fiber。从父到子
- 如果next === null,调用completeUnitOfWork,归阶段,从子到父。
- react16通过循环来模拟15的递归处理阶段。beginWork就像是递阶段,completeUnitOfWork就像是归阶段。但他们是可以中断的。
当所有fiber处理完成之后,表示render阶段完成,要进入commit阶段了。
render阶段进入commit阶段。
workLoopConcurrent执行完毕之后,会判断workInProgress是否等于null。如果等于null,表示Reconciler工作完成,要开启commit阶段了。
function renderRootConcurrent(root: FiberRoot, lanes: Lanes)
// 构建workInporgressFiber 树及rootFiber
prepareFreshStack(root, lanes);
do
try
// 真正执行任务
workLoopConcurrent();
break;
catch (thrownValue)
handleError(root, thrownValue);
while (true);
// Check if the tree has completed.
// 判断是否render完毕
if (workInProgress !== null)
return RootInProgress;
else
// Set this to null to indicate there's no in-progress render.
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
// Return the final exit status.
return workInProgressRootExitStatus;
如上,renderRootConcurrent会返回一个状态,RooInprogress就表示当前还在调度之中,而当workInprogress为null,就返回workInProgressRootExitStatus退出的状态。
而我们知道performConcurrentWorkOnRoot会根据renderRootConcurrent返回的状态决定是否进入commit阶段
function performConcurrentWorkOnRoot(root, didTimeout)
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
if (exitStatus !== RootInProgress)
...
// 将构建好的rootfiber存储到FiberRoot上
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
//完成了render阶段之后,开启commit阶段
finishConcurrentRender(root, exitStatus, lanes);
// 每次执行完performConcurrentWorkOnRoot都会调用ensureRootIsScheduled来判断当前是否有更高优先级的任务需要调度
ensureRootIsScheduled(root, now());
//如果没有更高优先级或者当前任务就是最高优先级的,继续返回该任务
if (root.callbackNode === originalCallbackNode)
// performConcurrentWorkOnRoot是ScheduleCallback注册的函数,而ScheduleCallback执行的时候,需要通过返回来确定该任务是否继续执行
// 这里通过ensureRootIsScheduled调度之后,发现root上面挂载的任务还是当前这个任务,表示当前的任务依然是最高优先级的。
// 所以,需要返回当前的任务给ScheduleCallback,以表示当前任务依然是最高优先级,需要执行。
return performConcurrentWorkOnRoot.bind(null, root);
//当调用ensureRootIsScheduled调度之后,如果有更高优先级的,或者任务都执行完毕了,那么这里返回null给scheduleCallback
// 表示当前任务已经结束,当Schedule执行注册的函数performConcurrentWorkOnRoot,结果是Null的时候,他会认为该任务已经结束。
// 会将该任务从最小堆中取出,然后继续调度,看有没有更高优先级的任务,注意,Schedule和React里面有各自的调度系统
return null;
如上,performConcurrentWorkOnRoot,在确定退出状态是结束状态的时候,就会调用finishConcurrentRender,顾名思义,已经完成了并发的render工作。
而finishConcuurentRender的工作就是
function finishConcurrentRender(root, exitStatus, lanes)
switch (exitStatus)
case RootInProgress:
case RootFatalErrored:
throw new Error("Root did not complete. This is a bug in React.");
....
case RootCompleted:
// The work completed. Ready to commit.
commitRoot(root, workInProgressRootRecoverableErrors);
break;
default:
throw new Error("Unknown root exit status.");
根据传入的状态,调用commitRoot函数,开启commit阶段,commit阶段是不可中断的。
commitRoot
commitRoot是commit阶段的入口
function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>)
const previousUpdateLanePriority = getCurrentUpdatePriority(); // 获取当前优先级保存
const prevTransition = ReactCurrentBatchConfig.transition;
try
ReactCurrentBatchConfig.transition = null;
setCurrentUpdatePriority(DiscreteEventPriority); // 修改当前优先级
// 因为commit阶段是不可被打断的,所以优先级绝对是最高的
commitRootImpl(root, recoverableErrors, previousUpdateLanePriority);
finally
ReactCurrentBatchConfig.transition = prevTransition;
setCurrentUpdatePriority(previousUpdateLanePriority); //恢复之前的优先级
return null;
因为commit阶段是不可被打断的,所以执行commit阶段之前,优先级要设为最高优先级。
commitRootImpl
commitRootImpl是commit阶段主要执行的函数,来看看他做了什么
function commitRootImpl(
root: FiberRoot,
recoverableErrors: null | Array<mixed>,
renderPriorityLevel: EventPriority
)
// --------before-mutation-之前的阶段-start-------
const finishedWork = root.finishedWork; // rootFiber
const lanes = root.finishedLanes; //优先级
// 重置FiberRoot的属性
root.finishedWork = null;
root.finishedLanes = NoLanes以上是关于react源码学习的主要内容,如果未能解决你的问题,请参考以下文章
React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信)
第十七篇:特别的事件系统:React 事件与 DOM 事件有何不同?
第十七篇:特别的事件系统:React 事件与 DOM 事件有何不同?