0%

react18.2源码分析(一) 应用加载

1. 入口 - createRoot API

createRoot 允许在浏览器的 DOM 节点创建根节点以显示 React 组件。

1
2
3
4
import { createRoot } from "react-dom/client";
import jsx from "./pages/ExamplePage";
const root = createRoot(document.getElementById("root"));
root.render(jsx);

createRoot 接收两个参数 containeroptions,返回一个 RootType 类型,即 ReactDOMRoot 的实例。

packages\react-dom\src\client\ReactDOMRoot.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
export type RootType = {
render(children: ReactNodeList): void,
unmount(): void,
_internalRoot: FiberRoot | null,
};

export type CreateRootOptions = {
unstable_strictMode?: boolean,
unstable_concurrentUpdatesByDefault?: boolean,
unstable_transitionCallbacks?: TransitionTracingCallbacks,
identifierPrefix?: string,
onRecoverableError?: (error: mixed) => void,
};

export function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions, // 有两个参数可用,其余的属于非稳定值,不要使用
): RootType {
// 1. 检查container是否是DOM
if (!isValidContainer(container)) {
throw new Error('createRoot(...): Target container is not a DOM element.');
}

warnIfReactDOMContainerInDEV(container);

let isStrictMode = false;
let concurrentUpdatesByDefaultOverride = false;
let identifierPrefix = '';
let onRecoverableError = defaultOnRecoverableError;
let transitionCallbacks = null;

// 2. 检查options
if (options !== null && options !== undefined) {
if (options.unstable_strictMode === true) {
isStrictMode = true;
}
if (
allowConcurrentByDefault &&
options.unstable_concurrentUpdatesByDefault === true
) {
concurrentUpdatesByDefaultOverride = true;
}
if (options.identifierPrefix !== undefined) {
identifierPrefix = options.identifierPrefix;
}
if (options.onRecoverableError !== undefined) {
onRecoverableError = options.onRecoverableError;
}
if (options.unstable_transitionCallbacks !== undefined) {
transitionCallbacks = options.unstable_transitionCallbacks;
}
}

// 3. 创建FiberRoot
const root: FiberRoot = createContainer(
container,
ConcurrentRoot,
null,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError,
transitionCallbacks,
);
// 4. 标记Container是根Fiber
markContainerAsRoot(root.current, container);
Dispatcher.current = ReactDOMClientDispatcher;

// comment nodes 已弃用,这里是为了兼容FB老代码 https://github.com/facebook/react/pull/24110
const rootContainerElement: Document | Element | DocumentFragment =
container.nodeType === COMMENT_NODE
? (container.parentNode: any)
: container;
// 5. 从Container层监听listenToAllSupportedEvents
listenToAllSupportedEvents(rootContainerElement);

// 6. 返回ReactDOMRoot实例
return new ReactDOMRoot(root);
}

1. 检查container是否是DOM

如果不是,则抛出错误提示。

1
2
3
if (!isValidContainer(container)) {
throw new Error('createRoot(...): Target container is not a DOM element.');
}

2. 检查options

⽬前⽂档中有两个参数可⽤: onRecoverableError 与 identifierPrefix 。

但是源码中实际上还有⼀些 unstable 值,属于⾮稳定值,不要使⽤.

1
2
3
4
5
6
7
export type CreateRootOptions = {
unstable_strictMode?: boolean,
unstable_concurrentUpdatesByDefault?: boolean,
unstable_transitionCallbacks?: TransitionTracingCallbacks,
identifierPrefix?: string,
onRecoverableError?: (error: mixed) => void,
};

3. createContainer 创建 FiberRoot, 即root

这⾥的 containerInfo 就是根 dom 节点。(就是那个 id 为 root 的div)。这个变量在 createRoot ⾥叫 container ,到这⾥换名成了 containerInfo

packages\react-reconciler\src\ReactFiberReconciler.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export function createContainer(
containerInfo: Container,
tag: RootTag,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onRecoverableError: (error: mixed) => void,
transitionCallbacks: null | TransitionTracingCallbacks,
): OpaqueRoot {
const hydrate = false;
const initialChildren = null;
return createFiberRoot(
containerInfo,
tag,
hydrate,
initialChildren,
hydrationCallbacks,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError,
transitionCallbacks,
null,
);
}

createFiberRoot 创建并返回 FiberRoot

packages\react-reconciler\src\ReactFiberRoot.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
export function createFiberRoot(
containerInfo: Container,
tag: RootTag,
hydrate: boolean,
initialChildren: ReactNodeList,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
// TODO: We have several of these arguments that are conceptually part of the
// host config, but because they are passed in at runtime, we have to thread
// them through the root constructor. Perhaps we should put them all into a
// single type, like a DynamicHostConfig that is defined by the renderer.
identifierPrefix: string,
onRecoverableError: null | ((error: mixed) => void),
transitionCallbacks: null | TransitionTracingCallbacks,
formState: ReactFormState<any, any> | null,
): FiberRoot {
// 1. fiberRoot 表示Fiber数据结构对象, 是Fiber数据结构中的最外层对象。
const root: FiberRoot = (new FiberRootNode(
containerInfo,
tag,
hydrate,
identifierPrefix,
onRecoverableError,
formState,
): any);
// 2. rootFiber表示组件挂载点对应的Fiber对象,比如React应用中默认的组件挂载点就是id为root的div。
const uninitializedFiber: Fiber = createHostRootFiber(
tag,
isStrictMode,
concurrentUpdatesByDefaultOverride,
);
// 3. 循环构造root 和 uninitializedFiber
// fiberRoot包含rootFiber,在fiberRoot对象中有一个current属性,存储rootFiber。
root.current = uninitializedFiber;
// rootFiber指向fiberRoot,在rootFiber对象中有一个stateNode属性,指向fiberRoot。
uninitializedFiber.stateNode = root;

// 在React应用中,可支持多个根节点fiberRoot,每一个 fiberRoot 下可以有多个 rootFiber ,因为 render 方法是可以调用多次的。
// FiberRoot 会记录应用的更新信息,比如协调器在完成工作后,会将工作成果存储在fiberRoot中。

if (enableCache) {
// sy
const initialCache = createCache();
retainCache(initialCache);

// The pooledCache is a fresh cache instance that is used temporarily
// for newly mounted boundaries during a render. In general, the
// pooledCache is always cleared from the root at the end of a render:
// it is either released when render commits, or moved to an Offscreen
// component if rendering suspends. Because the lifetime of the pooled
// cache is distinct from the main memoizedState.cache, it must be
// retained separately.
root.pooledCache = initialCache;
retainCache(initialCache);
const initialState: RootState = {
element: initialChildren,
isDehydrated: hydrate,
cache: initialCache,
};
uninitializedFiber.memoizedState = initialState;
} else {
const initialState: RootState = {
element: initialChildren,
isDehydrated: hydrate,
cache: (null: any), // not enabled yet
};
uninitializedFiber.memoizedState = initialState;
}

// 4. 初始化 initializeUpdateQueue
initializeUpdateQueue(uninitializedFiber);

return root;
}

3.1 实例化 FiberRootNode,创建 FiberRoot

packages\react-reconciler\src\ReactFiberRoot.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
function FiberRootNode(
this: $FlowFixMe,
containerInfo: any,
tag,
hydrate: any,
identifierPrefix: any,
onRecoverableError: any,
formState: ReactFormState<any, any> | null,
) {
this.tag = tag;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.current = null;
this.pingCache = null;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.cancelPendingCommit = null;
this.context = null;
this.pendingContext = null;
this.next = null;
this.callbackNode = null;
this.callbackPriority = NoLane;
this.expirationTimes = createLaneMap(NoTimestamp);

this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.finishedLanes = NoLanes;
this.errorRecoveryDisabledLanes = NoLanes;
this.shellSuspendCounter = 0;

// 记录当前更新与其他更新之间的关联性,即它们之间存在依赖或相关性。
// 当一个更新被触发时,React 会根据其依赖关系计算出 entangledLanes,这些 entangledLanes 表示与当前更新相关联的其他更新的 Lanes。
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes);

this.hiddenUpdates = createLaneMap(null);

this.identifierPrefix = identifierPrefix;
this.onRecoverableError = onRecoverableError;

if (enableCache) {
this.pooledCache = null;
this.pooledCacheLanes = NoLanes;
}

if (enableSuspenseCallback) {
this.hydrationCallbacks = null;
}

this.formState = formState;

this.incompleteTransitions = new Map();
if (enableTransitionTracing) {
this.transitionCallbacks = null;
const transitionLanesMap = (this.transitionLanes = []);
for (let i = 0; i < TotalLanes; i++) {
transitionLanesMap.push(null);
}
}

if (enableProfilerTimer && enableProfilerCommitHooks) {
this.effectDuration = 0;
this.passiveEffectDuration = 0;
}

if (enableUpdaterTracking) {
this.memoizedUpdaters = new Set();
const pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []);
for (let i = 0; i < TotalLanes; i++) {
pendingUpdatersLaneMap.push(new Set());
}
}
}

3.2 createHostRootFiber 创建原生标签的根 Fiber

注意这⾥创建的是 Fiber 只是属于根部的 Fiber 。和 3.1 的 FiberRoot 不同,FiberRootFiber 是两个类型。

packages\react-reconciler\src\ReactFiber.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
export function createHostRootFiber(
tag: RootTag,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
): Fiber {
let mode;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode;
if (isStrictMode === true) {
mode |= StrictLegacyMode | StrictEffectsMode;
}
if (
// We only use this flag for our repo tests to check both behaviors.
forceConcurrentByDefaultForTesting
) {
mode |= ConcurrentUpdatesByDefaultMode;
} else if (
// Only for internal experiments.
allowConcurrentByDefault &&
concurrentUpdatesByDefaultOverride
) {
mode |= ConcurrentUpdatesByDefaultMode;
}
} else {
mode = NoMode;
}

if (enableProfilerTimer && isDevToolsPresent) {
// Always collect profile timings when DevTools are present.
// This enables DevTools to start capturing timing at any point–
// Without some nodes in the tree having empty base times.
mode |= ProfileMode;
}

return createFiber(HostRoot, null, null, mode);
}
1
2
3
4
5
6
7
8
function createFiber(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
): Fiber {
return new FiberNode(tag, pendingProps, key, mode);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function FiberNode(
this: $FlowFixMe,
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag; // 组件类型
this.key = key; // 组件props上的key
this.elementType = null; // ReactElement.type 组件的dom类型, 比如`div, p`
this.type = null; // 异步组件resolved之后返回的内容
this.stateNode = null; // 在浏览器环境对应dom节点

// Fiber
this.return = null; // 指向父节点
this.child = null; // 孩子节点
this.sibling = null; // 兄弟节点, 兄弟节点的return指向同一个父节点
this.index = 0;

this.ref = null;
this.refCleanup = null;

this.pendingProps = pendingProps; // 新的props
this.memoizedProps = null; // 上一次渲染完成的props
this.updateQueue = null; // 组件产生的update信息会放在这个队列
this.memoizedState = null; // 上一次渲染完成的state
this.dependencies = null;

this.mode = mode;

// Effects
this.flags = NoFlags; // 相当于之前的effectTag, 记录side effect类型
this.subtreeFlags = NoFlags;
this.deletions = null;

this.lanes = NoLanes; // 优先级相关
this.childLanes = NoLanes; // 优先级相关
this.alternate = null; // 对应的是current fiber
}

3.3 循环构造 root 和 uninitializedFiber

root.currentFiber
uninitializedFiber.stateNode 是根 FiberRoot

1
2
root.current = uninitializedFiber; // Fiber
uninitializedFiber.stateNode = root; // FiberRoot

3.4 初始化 initializeUpdateQueue

类似fiber,update queues也是成对出现的,一个已经完成的即对应目前页面,一个正在工作中的。

packages\react-reconciler\src\ReactFiberClassUpdateQueue.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
export type Update<State> = {
lane: Lane,

tag: 0 | 1 | 2 | 3,
payload: any,
callback: (() => mixed) | null,

next: Update<State> | null,
};

export type SharedQueue<State> = {
pending: Update<State> | null, // 单向循环链表
lanes: Lanes,
// 如果类组件是Activity(以前叫OffScreen)的后代组件,需要延迟执行的其setState的callback。这里是先暂时收集,commit阶段提交
// Activity目前还是unstable,了解即可~
hiddenCallbacks: Array<() => mixed> | null,
};

export type UpdateQueue<State> = {
baseState: State,
// 单链表 firstBaseUpdate->...->lastBaseUpdate
firstBaseUpdate: Update<State> | null,
lastBaseUpdate: Update<State> | null,
shared: SharedQueue<State>,
callbacks: Array<() => mixed> | null,
};

// 这里初始化fiber.updateQueue
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
lanes: NoLanes,
hiddenCallbacks: null,
},
callbacks: null,
};
fiber.updateQueue = queue;
}

4. markContainerAsRoot 标记 Container 是根 Fiber

这个函数给container根DOM节点赋值根Fiber

packages\react-dom-bindings\src\client\ReactDOMComponentTree.js

1
2
3
4
5
6
const randomKey = Math.random().toString(36).slice(2);
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
// 标记根节点
export function markContainerAsRoot(hostRoot: Fiber, node: Container): void {
node[internalContainerInstanceKey] = hostRoot;
}

这个属性值在函数中用于 getClosestInstanceFromNode 和 getInstanceFromNode 中会用于根据根DOM去Fiber值。

对应的还有两个函数:

1
2
3
4
5
6
7
8
// 取消标记,在ReactDOMRoot.prototype.unmount函数里调用
export function unmarkContainerAsRoot(node: Container): void {
node[internalContainerInstanceKey] = null;
}
// 检查是否被标记为根节点
export function isContainerMarkedAsRoot(node: Container): boolean {
return !!node[internalContainerInstanceKey];
}

5. 从Container层监听listenToAllSupportedEvents

react事件比较复杂,后续事件文章展开写。

6. 返回ReactDOMRoot实例

1
return new ReactDOMRoot(root);

ReactDOMRoot 函数:

1
2
3
function ReactDOMRoot(internalRoot: FiberRoot) {
this._internalRoot = internalRoot;
}

2. 加载 - 执行 render 函数

createRoot 会返回 RootType 类型,即 ReactDOMRoot 的实例。

packages\react-dom\src\client\ReactDOMRoot.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export type RootType = {
render(children: ReactNodeList): void,
unmount(): void,
_internalRoot: FiberRoot | null,
};

function ReactDOMRoot(internalRoot: FiberRoot) {
this._internalRoot = internalRoot;
}

ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
function (children: ReactNodeList): void {
const root = this._internalRoot;
if (root === null) {
throw new Error('Cannot update an unmounted root.');
}
updateContainer(children, root, null, null);
};

updateContainer 源码:

packages\react-reconciler\src\ReactFiberReconciler.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
// ! 1. 获取current和lane
const current = container.current;
const lane = requestUpdateLane(current); // 页面初次渲染,defaultLane 32

// ! 2. 创建update
const update = createUpdate(lane);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element};

// 页面初次渲染,React18中已取消callback,只有老版本有效
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}

// ! 3. 将update入队管理
const root = enqueueUpdate(current, update, lane);

if (root !== null) {
// ! 4. 调度更新
scheduleUpdateOnFiber(root, current, lane);
// ! 5. 处理transitions,非紧急更新
entangleTransitions(root, current, lane);
}

return lane;
}

2.1 获取 current 和 lane

lane 是用于标识 update 优先级,一种标识 update 优先级的机制。每个 update 都会被分配一个或者多个 lane,以确定其在更新队列中的优先级顺序。

packages\react-reconciler\src\ReactFiberWorkLoop.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
export function requestUpdateLane(fiber: Fiber): Lane {
// Special cases
const mode = fiber.mode;
// 1. 非ConcurrentMode模式 2. 目前不支持
if ((mode & ConcurrentMode) === NoMode) {
return (SyncLane: Lane);
} else if (
(executionContext & RenderContext) !== NoContext &&
workInProgressRootRenderLanes !== NoLanes
) {
// This is a render phase update. These are not officially supported. The
// old behavior is to give this the same "thread" (lanes) as
// whatever is currently rendering. So if you call `setState` on a component
// that happens later in the same render, it will flush. Ideally, we want to
// remove the special case and treat them as if they came from an
// interleaved event. Regardless, this pattern is not officially supported.
// This behavior is only a fallback. The flag only exists until we can roll
// out the setState warning, since existing code might accidentally rely on
// the current behavior.
return pickArbitraryLane(workInProgressRootRenderLanes);
}

// 普通更新与非紧急更新(18)
const transition = requestCurrentTransition();
// 如果有transition
if (transition !== null) {
const actionScopeLane = peekEntangledActionLane();
return actionScopeLane !== NoLane
? // We're inside an async action scope. Reuse the same lane.
actionScopeLane
: // We may or may not be inside an async action scope. If we are, this
// is the first update in that scope. Either way, we need to get a
// fresh transition lane.
requestTransitionLane(transition);
}

// TODO: Move this type conversion to the event priority module.
// React内部的一些update,比如flushSync,会通过上下文变量来跟踪其优先级
const updateLane: Lane = (getCurrentUpdatePriority(): any);
if (updateLane !== NoLane) {
// ? sy setState click 2
return updateLane;
}

// TODO: Move this type conversion to the event priority module.
// React外部的update,根据事件类型,向当前环境获取对应的优先级。
const eventLane: Lane = (getCurrentEventPriority(): any);
return eventLane;
}
1.1.1 getCurrentEventPriority

packages\react-dom-bindings\src\client\ReactFiberConfigDOM.js

1
2
3
4
5
6
7
8
export function getCurrentEventPriority(): EventPriority {
const currentEvent = window.event;
if (currentEvent === undefined) {
// ? sy 页面初次渲染
return DefaultEventPriority;
}
return getEventPriority(currentEvent.type);
}
1.1.2 DefaultEventPriority、getCurrentUpdatePriority

packages\react-reconciler\src\ReactEventPriorities.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export opaque type EventPriority = Lane;

// 优先级从上往下,越来越低
export const DiscreteEventPriority: EventPriority = SyncLane; // 2
export const ContinuousEventPriority: EventPriority = InputContinuousLane; // 8
export const DefaultEventPriority: EventPriority = DefaultLane; // 页面初次渲染的lane 32, transition
export const IdleEventPriority: EventPriority = IdleLane;

let currentUpdatePriority: EventPriority = NoLane;

export function getCurrentUpdatePriority(): EventPriority {
return currentUpdatePriority;
}

export function setCurrentUpdatePriority(newPriority: EventPriority) {
currentUpdatePriority = newPriority;
}

2.2 创建update(createUpdate)

packages\react-reconciler\src\ReactFiberClassUpdateQueue.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export type Update<State> = {
lane: Lane,

tag: 0 | 1 | 2 | 3,
payload: any,
callback: (() => mixed) | null,

next: Update<State> | null,
};
export function createUpdate(lane: Lane): Update<mixed> {
const update: Update<mixed> = {
lane,

tag: UpdateState,
payload: null,
callback: null,

next: null,
};
return update;
}

2.3 update入队

createRoot(root).render() 阶段与类组件的 setStateforceUpdate 阶段最开始调⽤的是 ReactFiberClassUpdateQueue.js 中的 enqueueUpdate

packages\react-reconciler\src\ReactFiberClassUpdateQueue.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
export function enqueueUpdate<State>(
fiber: Fiber,
update: Update<State>,
lane: Lane,
): FiberRoot | null {
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return null;
}

const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;

// 类组件旧的生命周期相关的update
if (isUnsafeClassRenderPhaseUpdate(fiber)) {
// This is an unsafe render phase update. Add directly to the update
// queue so we can process it immediately during the current render.
const pending = sharedQueue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;

// Update the childLanes even though we're most likely already rendering
// this fiber. This is for backwards compatibility in the case where you
// update a different component during render phase than the one that is
// currently renderings (a pattern that is accompanied by a warning).
return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
// sy
return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
}
}
1.3.1 enqueueConcurrentClassUpdate

packages\react-reconciler\src\ReactFiberConcurrentUpdates.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ClassUpdate | HookUpdate;
export type ConcurrentUpdate = {
next: ConcurrentUpdate,
lane: Lane,
};
// ClassQueue | HookQueue;
type ConcurrentQueue = {
pending: ConcurrentUpdate | null,
};
// 如果渲染正在进行中,并且收到来自并发事件的更新,我们会等到当前的渲染结束(无论是完成还是被中断)之后再将其添加到 fiber 队列中。
// 将其推送到这个数组中,这样我们以后就可以访问queue、fiber、update等。
const concurrentQueues: Array<any> = [];
let concurrentQueuesIndex = 0;

let concurrentlyUpdatedLanes: Lanes = NoLanes;
export function enqueueConcurrentClassUpdate<State>(
fiber: Fiber,
queue: ClassQueue<State>,
update: ClassUpdate<State>,
lane: Lane,
): FiberRoot | null {
const concurrentQueue: ConcurrentQueue = (queue: any);
const concurrentUpdate: ConcurrentUpdate = (update: any);
// ! 1. update入队
enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
// ! 2. 返回FiberRoot
return getRootForUpdatedFiber(fiber);
}
1.3.1.1 enqueueUpdate

update 存储到 concurrentQueues 中,虽然这个函数也叫 enqueueUpdate

这⾥的 enqueueUpdate 是数字式存储,并且是依次存储 fiberqueueupdatelane,下次依然这个顺序,最后执⾏处理的时候也要按照这个规律取值。

React 源码中其它地⽅这种结构都是对象式写法,这⾥⽐较罕⻅地写了这个结构。

packages\react-reconciler\src\ReactFiberConcurrentUpdates.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function enqueueUpdate(
fiber: Fiber,
queue: ConcurrentQueue | null,
update: ConcurrentUpdate | null,
lane: Lane,
) {
// Don't update the `childLanes` on the return path yet. If we already in
// the middle of rendering, wait until after it has completed.
concurrentQueues[concurrentQueuesIndex++] = fiber;
concurrentQueues[concurrentQueuesIndex++] = queue;
concurrentQueues[concurrentQueuesIndex++] = update;
concurrentQueues[concurrentQueuesIndex++] = lane;

concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane);

// The fiber's `lane` field is used in some places to check if any work is
// scheduled, to perform an eager bailout, so we need to update it immediately.
// TODO: We should probably move this to the "shared" queue instead.
fiber.lanes = mergeLanes(fiber.lanes, lane);
const alternate = fiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}
}
1.3.1.2 getRootForUpdatedFiber 找到 FiberRoot

从 sourceFiber 开始,找到根 Fiber,返回其 stateNode,即 FiberRoot。

packages\react-reconciler\src\ReactFiberConcurrentUpdates.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function getRootForUpdatedFiber(sourceFiber: Fiber): FiberRoot | null {
// 如果循环超过限制次数(50次),抛出错误。比如在类组件的render函数里执行setState
throwIfInfiniteUpdateLoopDetected();

// When a setState happens, we must ensure the root is scheduled. Because
// update queues do not have a backpointer to the root, the only way to do
// this currently is to walk up the return path. This used to not be a big
// deal because we would have to walk up the return path to set
// the `childLanes`, anyway, but now those two traversals happen at
// different times.
// TODO: Consider adding a `root` backpointer on the update queue.
// __DEV__,检查是否有未挂载的Fiber,如Can't perform a React state update on a component that hasn't mounted yet.
detectUpdateOnUnmountedFiber(sourceFiber, sourceFiber);
let node = sourceFiber;
let parent = node.return;
// 循环往上查找,找到根节点
while (parent !== null) {
detectUpdateOnUnmountedFiber(sourceFiber, node);
node = parent;
parent = node.return;
}

// 根节点一定是HostRoot,返回根节点的stateNode,即FiberRoot
return node.tag === HostRoot ? (node.stateNode: FiberRoot) : null;
}

2.4 scheduleUpdateOnFiber 调度更新

调度 update。

具体查看 scheduleUpdateOnFiber调度更新 文章。

1
2
3
4
5
6
7
export function scheduleUpdateOnFiber(
root: FiberRoot,
fiber: Fiber,
lane: Lane,
) {
// ...
}

2.5 entangleTransitions 非紧急更新

处理非紧急更新 Transitions。查看后续文章。