简述
react-reconsiler
模块是React中最核心的模块之一,也是最复杂的模块之一。其中包括React Fiber框架、React Hooks等的实现。
本文主要讲述其中React中Class组件初始化过程和更新过程
Class组件初始化过程
在Class组件初始化过程中,React为其创建一个FiberNode
实例,其实也是一个WIP(workInProcess)。
(1)BeginWork
过程
BeginWork
过程是处理WIP的第一阶段。在这个过程中,首先是计算组件的props属性,并执行updateClassComponent
函数。
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
}
在updateClassComponent
中,将完成类实例的创建,以及挂载实例。
// ....
// 构建类组件实例
constructClassInstance(workInProgress, Component, nextProps);
mountClassInstance(
workInProgress,
Component,
nextProps,
renderExpirationTime,
);
shouldUpdate = true;
类实例创建过程中,将给实例的updater
属性进行赋值,这个很重要,后续组件调用setState
更新状态,实际上是调用的该对象的方法。
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
instance.updater = classComponentUpdater;
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
setInstance(instance, workInProgress);
// ...
}
挂载实例过程中:
- 首先计算实例的上下文(
context
)
const contextType = ctor.contextType;
if (typeof contextType === 'object' && contextType !== null) {
instance.context = readContext(contextType);
} else if (disableLegacyContext) {
instance.context = emptyContextObject;
} else {
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
instance.context = getMaskedContext(workInProgress, unmaskedContext);
}
- 然后,计算实例的状态(
state
)
processUpdateQueue(workInProgress, newProps, instance, renderExpirationTime);
instance.state = workInProgress.memoizedState;
- 接着,调用类组件的
getDerivedStateFromProps
方法,并重新给类实例的state
赋值,
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
instance.state = workInProgress.memoizedState;
}
- 再然后,调用类实例的
componentWillMount
函数(或UNSAFE_componentWillMount
),并重新计算state
(因为有可能在改函数中调用了setState
,虽然不建议这么做)。
if (
typeof ctor.getDerivedStateFromProps !== 'function' &&
typeof instance.getSnapshotBeforeUpdate !== 'function' &&
(typeof instance.UNSAFE_componentWillMount === 'function' ||
typeof instance.componentWillMount === 'function')
) {
callComponentWillMount(workInProgress, instance);
// If we had additional state updates during this life-cycle, let's
// process them now.
processUpdateQueue(
workInProgress,
newProps,
instance,
renderExpirationTime,
);
instance.state = workInProgress.memoizedState;
}
- 最后标记类实例是否有
componentDidMount
函数。
if (typeof instance.componentDidMount === 'function') {
workInProgress.effectTag |= Update;
}
挂载类实例后,React调用类组件的render
方法生成组件的子节点,并为子节点生成下一个WIP。
nextChildren = instance.render();
// ...
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
(2)completeWork
过程
在完成所有子组件的WIP后,类组件将进入completeWork
过程。
if (next === null) {
// If this doesn't spawn new work, complete the current work.
next = completeUnitOfWork(unitOfWork);
}
类组件的这个阶段比较简单,仅仅只需要处理上下文。
case ClassComponent: {
const Component = workInProgress.type;
if (isLegacyContextProvider(Component)) {
popLegacyContext(workInProgress);
}
return null;
}
(3)commitWork
过程
在完成所有WIP后,React调用finishSyncRender
进入commitWork过程,在这个过程中将调用类实例的componentDidMount
函数。
function commitLifeCycles(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedExpirationTime: ExpirationTime,
): void {
switch (finishedWork.tag) {
// ...
case ClassComponent: {
const instance = finishedWork.stateNode;
if (finishedWork.effectTag & Update) {
if (current === null) {
// ...
// 更新过程调用componentDidMount
instance.componentDidMount();
stopPhaseTimer();
} else {
// 更新过程调用componentDidUpdate
// ...
}
}
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
commitUpdateQueue(finishedWork, updateQueue, instance);
}
return;
}
Class组件更新过程
Class组件更新一般有以下几种原因:
- 父组件更新
- 调用Class组件的
setState
方法更新
Class组件的setState
方法中更新组件的状态其实是调用其updater
属性的enqueueSetState
(前面有提到过设置updater
属性)。
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
- 调用Class组件的
forceUpdate
方法进行更新
Class组件的forceUpdate
方法中更新组件的状态调用的是其updater
属性的enqueueForceUpdate
。
enqueueSetState
方法和enqueueForceUpdate
方法中的逻辑和React初始化的逻辑类似,都会创建一个Update
实例,然后调用enqueueUpdate
函数以及scheduleWork
函数进行更新。
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const currentTime = requestCurrentTimeForUpdate();
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);
const update = createUpdate(expirationTime, suspenseConfig);
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleWork(fiber, expirationTime);
},
enqueueForceUpdate(inst, callback) {
const fiber = getInstance(inst);
const currentTime = requestCurrentTimeForUpdate();
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);
const update = createUpdate(expirationTime, suspenseConfig);
update.tag = ForceUpdate;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'forceUpdate');
}
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleWork(fiber, expirationTime);
},
注:如何判断setState
同步和异步更新state
,一般可以根据触发调用setState
函数进行更新的是React的内部机制还是外部原因(如,setTimeout
的回调函数中调用setState
、网络请求响应的处理函数中调用setState
)。在React的内部机制中调用setState
是异步更新;由外部原因导致的一般是同步更新。
这里的React的内部机制包括一下几种情况:
- React事件处理过程中触发,例如:
<button onClick={this.onClick}>点我点我</button>
在onClick
中调用setState
。这是由于此处的click
事件被React代为处理了。
- 在初始化过程中执行的
componentWillMount
方法、componentDidMount
方法等生命周期钩子函数中调用setState
。
这是因为这两种情况都设置了executionContext
,使得executionContext
不会为NoContext
。
// React初始化中调用batchedUpdates,设置executionContext
export function batchedUpdates<A, R>(fn: A => R, a: A): R {
const prevExecutionContext = executionContext;
executionContext |= BatchedContext;
try {
return fn(a);
} finally {
executionContext = prevExecutionContext;
if (executionContext === NoContext) {
// Flush the immediate callbacks that were scheduled during this batch
flushSyncCallbackQueue();
}
}
}
// React事件处理过程调用batchedEventUpdates,设置executionContext
export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {
const prevExecutionContext = executionContext;
executionContext |= EventContext;
try {
return fn(a);
} finally {
executionContext = prevExecutionContext;
if (executionContext === NoContext) {
// Flush the immediate callbacks that were scheduled during this batch
flushSyncCallbackQueue();
}
}
}
Class组件更新过程和初始化过程大体一致,同样可以分BeginWork
、CompleteWork
、CommitWork
三个阶段。
BeginWork
阶段
由于在初始化的时候已经创建了组件实例,这个阶段调用updateClassInstance
更新类实例:
- 计算
props
const oldProps = workInProgress.memoizedProps;
instance.props =
workInProgress.type === workInProgress.elementType
? oldProps
: resolveDefaultProps(workInProgress.type, oldProps);
- 计算
context
const oldContext = instance.context;
const contextType = ctor.contextType;
let nextContext = emptyContextObject;
if (typeof contextType === 'object' && contextType !== null) {
nextContext = readContext(contextType);
} else if (!disableLegacyContext) {
const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
}
- 调用
componentWillReceiveProps
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
typeof instance.componentWillReceiveProps === 'function')
) {
if (oldProps !== newProps || oldContext !== nextContext) {
callComponentWillReceiveProps(
workInProgress,
instance,
newProps,
nextContext,
);
}
}
- 计算
state
const oldState = workInProgress.memoizedState;
let newState = (instance.state = oldState);
processUpdateQueue(workInProgress, newProps, instance, renderExpirationTime);
newState = workInProgress.memoizedState;
- 设置
componentDidUpdate
函数和getSnapshotBeforeUpdate
函数的标志位,在CompleteWork
或CommitWork
过程用到。
CompleteWork
阶段
与初始化过程一样。
CommitWork
阶段
在这个过程中将根据前面设置的标志位调用componentDidUpdate
和getSnapshotBeforeUpdate
:
function commitLifeCycles(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedExpirationTime: ExpirationTime,
): void {
switch (finishedWork.tag) {
// ...
case ClassComponent: {
const instance = finishedWork.stateNode;
if (finishedWork.effectTag & Update) {
if (current === null) {
// ...
// 初始化过程调用`componentDidMount`。
} else {
const prevProps =
finishedWork.elementType === finishedWork.type
? current.memoizedProps
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
const prevState = current.memoizedState;
startPhaseTimer(finishedWork, 'componentDidUpdate');
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
// ...
// 调用componentDidUpdate
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
stopPhaseTimer();
}
}
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
// ...
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
commitUpdateQueue(finishedWork, updateQueue, instance);
}
return;
}
// ...
}
}
function commitBeforeMutationLifeCycles(
current: Fiber | null,
finishedWork: Fiber,
): void {
switch (finishedWork.tag) {
// ...
case ClassComponent: {
if (finishedWork.effectTag & Snapshot) {
if (current !== null) {
// ...
// 调用getSnapshotBeforeUpdate
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
return;
}
// ...
}
}
网友评论