前言
紧接上一篇的内容React源码解析之Fiber,上一篇分析了Fiber树的创建,接下来则是进入Fiber的节点的创建和更新。
正文
对Fiber的节点的创建和更新会调用performUnitOfWork
函数,这个函数中主要是调用两个方法:beginWork
,completeUnitOfWork
,本章先只分析beginWork
。
beginWork
的主要工作,则是从root fiber开始向下深度遍历构建workInprogress树,进行diff算法确定需要更新的fiber的最终状态。
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
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;
}
记得在React源码解析之Fiber中,最后形成的Fiber树结构如下:
在调用performUnitOfWork
函数时传入workInProgress
,也就是root fiber
,然后在performUnitOfWork
函数中获取workInProgress
树上的root fiber
的alternate
属性,也就是current树上的root fiber
,后面会以current树上对应workInProgress
树上的Fiber节点是否存在来判断该Fiber节点是更新,还是创建。简单理解就是Fiber节点上alternate
属性是否为null,不为null则是更新,为null则是创建。
然后调用beginWork
函数传入,两颗树上对应的Fiber节点,current
是current树上的Fiber节点,unitOfWork
是workInProgress
树上的Fiber节点,以及渲染的Lane优先级集合subtreeRenderLanes
。
beginWork(current, unitOfWork, subtreeRenderLanes);
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
...
// 判断current是否为null,不为null表示更新,为null则表示是创建
if (current !== null) {
const oldProps = current.memoizedProps; // 上一次更新的props
const newProps = workInProgress.pendingProps; // 本次更新的props
// didReceiveUpdate 表示是否有新的props更新,有则会设置为true,没有则是false
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
// Force a re-render if the implementation changed due to hot reload:
(__DEV__ ? workInProgress.type !== current.type : false)
) {
didReceiveUpdate = true;
} else {
// checkScheduledUpdateOrContext函数检查当前fiber节点上的lanes是否存在于renderLanes中
// 存在则说明当前fiber节点需要更新,不存在则不需要更新则复用之前的节点
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
current,
renderLanes,
);
if (
!hasScheduledUpdateOrContext &&
(workInProgress.flags & DidCapture) === NoFlags
) {
didReceiveUpdate = false;
// 复用之前的节点
return attemptEarlyBailoutIfNoScheduledUpdate(
current,
workInProgress,
renderLanes,
);
}
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
didReceiveUpdate = true;
} else {
didReceiveUpdate = false;
}
}
} else {
didReceiveUpdate = false;
}
workInProgress.lanes = NoLanes;
switch (workInProgress.tag) {
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,
renderLanes,
);
}
...
}
}
首先判断current
是否为null
,如果为null
则说明是创建,不为null
,则会调用checkScheduledUpdateOrContext
函数检查当前fiber节点上的lanes是否存在于renderLanes中,存在则说明当前fiber节点需要更新返回true,不存在则不需要更新返回false。
function checkScheduledUpdateOrContext(
current: Fiber,
renderLanes: Lanes,
): boolean {
const updateLanes = current.lanes;
// 判断当前Fiber极端上的lanes是否存在于renderLanes上,存在则说明该Fiber节点需要更新,反之不需要
if (includesSomeLane(updateLanes, renderLanes)) {
return true;
}
...
return false;
}
当checkScheduledUpdateOrContext
函数返回false,则会调用attemptEarlyBailoutIfNoScheduledUpdate
函数,这个函数主要作用,则是复用fiber节点。
当checkScheduledUpdateOrContext
函数返回true,则会根据Fiber节点上tag属性来判断类型,我们以classComponent为例,则会调用updateClassComponent
:
function updateClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
nextProps: any,
renderLanes: Lanes,
) {
...
const instance = workInProgress.stateNode;
let shouldUpdate;
//根据组件stateNode(组件实例)的值是否为null,以此来判断应该创建组件还是更新组件
if (instance === null) {
if (current !== null) {
current.alternate = null;
workInProgress.alternate = null;
workInProgress.flags |= Placement;
}
// 实例化组件,将组件实例与对应的fiber节点相关联
constructClassInstance(workInProgress, Component, nextProps);
// 将fiber上的state和props更新至组件实例上
// 并且会检查是否声明了getDerivedStateFromProps生命周期函数
// 有的话则会调用并且使用getDerivedStateFromProps生命周期函数中返回的state来更新组件实例上的state
// 检查是否声明了componentDidmount生命周期,有的话则会收集标示添加到fiber的flags属性上
mountClassInstance(workInProgress, Component, nextProps, renderLanes);
// 创建组件肯定是需要更新的,所以直接为shouldUpdate赋值为true
shouldUpdate = true;
} else if (current === null) {
shouldUpdate = resumeMountClassInstance(
workInProgress,
Component,
nextProps,
renderLanes,
);
} else {
// 更新组件-updateClassInstance函数:
// 调用生命周期:getDerivedStateFromProps,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate
// 执行更新队列,把将要更新的state的值与老的state的值进行合并,合并完成和会把新的state的值挂载到workInProgress.memoizedState属性上
// 使用新的值分别更新组件实例中props,state的值
// 收集Effect:会判断当前类组件是否声明了生命周期函数:componentDidUpdate,getSnapshotBeforeUpdate,
// 声明了则会将相对应的flag添加到当前fiber节点的flags属性上
shouldUpdate = updateClassInstance(
current,
workInProgress,
Component,
nextProps,
renderLanes,
);
}
// finishClassComponent函数:
// 判断生命周期shouldComponentUpdate返回的值为false的话,则复用之前的fiber节点
// 为true则会调用render函数,获取使用最新的state和props生成的React element
// 调用reconcileChildren函数,这个函数主要作用是进行diff算法。
const nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderLanes,
);
return nextUnitOfWork;
}
创建类组件
可以看到首先会通过stateNode
属性获取类组件的实例,然后判断实例是否为null,为null则说明是创建,则需要去实例化组件:
// 实例化组件,将组件实例与对应的fiber节点相关联
constructClassInstance(workInProgress, Component, nextProps);
function constructClassInstance(
workInProgress: Fiber,
ctor: any,
props: any,
): any {
...
// 实例化组件
let instance = new ctor(props, context);
...
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
: null);
// 将fiber节点与组件实例相互关联,在之后更新时可复用
adoptClassInstance(workInProgress, instance);
...
return instance;
}
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
...
// fiber节点的stateNode属性指向该fiber节点对应的组件实例对象
workInProgress.stateNode = instance;
// 将组件实例与fiber关联在一起,instance._reactInternals = workInProgress
setInstance(instance, workInProgress);
}
调用constructClassInstance
时传入fiber,组件的class,和props,然后constructClassInstance
中通过实例化组件class获取组件实例,然后获取组件实例中的state属性赋值给workInProgress.memoizedState
,之后调用adoptClassInstance
函数,将fiber节点与组件实例相互关联。在下一次更新时,则可以通过instance._reactInternals
获取到组件实例对应的Fiber节点。
创建组件完成后,会调用mountClassInstance
函数:
function mountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): void {
...
const instance = workInProgress.stateNode;
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = emptyRefsObject;
initializeUpdateQueue(workInProgress);
...
// 检查当前组件是否声明了getDerivedStateFromProps生命周期函数
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
// 有声明的话则会调用并且使用getDerivedStateFromProps生命周期函数中返回的state来更新workInProgress.memoizedState
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
// 将更新了的state赋值给组件实例的state属性
instance.state = workInProgress.memoizedState;
}
//调用componentWillMount生命周期
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, renderLanes);
instance.state = workInProgress.memoizedState;
}
// 判断是否声明了componentDidMount声明周期,声明了则会添加标识Update至flags中,在commit阶段调用
if (typeof instance.componentDidMount === 'function') {
let fiberFlags: Flags = Update;
if (enableSuspenseLayoutEffectSemantics) {
fiberFlags |= LayoutStatic;
}
...
workInProgress.flags |= fiberFlags;
}
}
这个函数主要的作用是:
- fiber上的state和props更新至组件实例上,并且会检查是否声明了getDerivedStateFromProps生命周期函数,有的话则会调用并且使用getDerivedStateFromProps生命周期函数中返回的state来更新组件实例上的state和Fiber节点上的memoizedState。
- 检查是否声明了componentWillMount生命周期,有的话则会调用componentWillMount生命周期
- 检查是否声明了componentDidmount生命周期,有的话则会收集flag添加到fiber的flags属性上
调用完成后,因为是创建组件,肯定是需要更新的,所以直接为shouldUpdate赋值为true
更新类组件
更新组件,则会调用updateClassInstance
函数:
function updateClassInstance(
current: Fiber,
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): boolean {
const instance = workInProgress.stateNode;
cloneUpdateQueue(current, workInProgress);
const unresolvedOldProps = workInProgress.memoizedProps;
const oldProps =
workInProgress.type === workInProgress.elementType
? unresolvedOldProps
: resolveDefaultProps(workInProgress.type, unresolvedOldProps);
instance.props = oldProps;
const unresolvedNewProps = workInProgress.pendingProps;
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);
}
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
const hasNewLifecycles =
typeof getDerivedStateFromProps === 'function' ||
typeof instance.getSnapshotBeforeUpdate === 'function';
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
typeof instance.componentWillReceiveProps === 'function')
) {
if (
unresolvedOldProps !== unresolvedNewProps ||
oldContext !== nextContext
) {
callComponentWillReceiveProps(
workInProgress,
instance,
newProps,
nextContext,
);
}
}
resetHasForceUpdateBeforeProcessing();
const oldState = workInProgress.memoizedState;
let newState = (instance.state = oldState);
// 执行更新队列,把将要更新的state的值与老的state的值进行合并
// 合并完成和会把新的state的值挂载到workInProgress.memoizedState属性上
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
newState = workInProgress.memoizedState;
if (
unresolvedOldProps === unresolvedNewProps &&
oldState === newState &&
!hasContextChanged() &&
!checkHasForceUpdateAfterProcessing() &&
!(
enableLazyContextPropagation &&
current !== null &&
current.dependencies !== null &&
checkIfContextChanged(current.dependencies)
)
) {
if (typeof instance.componentDidUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Snapshot;
}
}
return false;
}
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
newState = workInProgress.memoizedState;
}
// checkHasForceUpdateAfterProcessing是获取是否是强制更新的标识,也就是调用的forceUpdate方法创建的更新
// checkShouldComponentUpdate是调用组件内声明的shouldComponentUpdate生命周期方法,判断是否需要更新
const shouldUpdate =
checkHasForceUpdateAfterProcessing() ||
checkShouldComponentUpdate(
workInProgress,
ctor,
oldProps,
newProps,
oldState,
newState,
nextContext,
) ||
(enableLazyContextPropagation &&
current !== null &&
current.dependencies !== null &&
checkIfContextChanged(current.dependencies));
if (shouldUpdate) {
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillUpdate === 'function' ||
typeof instance.componentWillUpdate === 'function')
) {
if (typeof instance.componentWillUpdate === 'function') {
instance.componentWillUpdate(newProps, newState, nextContext);
}
if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
}
}
if (typeof instance.componentDidUpdate === 'function') {
workInProgress.flags |= Update;
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
workInProgress.flags |= Snapshot;
}
} else {
if (typeof instance.componentDidUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Snapshot;
}
}
workInProgress.memoizedProps = newProps;
workInProgress.memoizedState = newState;
}
// 使用新的值分别更新组件中props,state的值
instance.props = newProps;
instance.state = newState;
instance.context = nextContext;
return shouldUpdate;
}
这个函数主要的作用:
- 会调用
componentWillReceiveProps
函数 - 通过调用
processUpdateQueue
执行更新队列,把将要更新的state的值与老的state的值进行合并,合并完成和会把新的state的值挂载到workInProgress.memoizedState属性上,然后使用新的值分别更新组件实例中props,state的值 - 调用
checkHasForceUpdateAfterProcessing
函数获取是否是强制更新的标识,也就是调用的forceUpdate方法创建的更新,checkShouldComponentUpdate
是调用组件内声明的shouldComponentUpdate
生命周期方法,判断是否需要更新 - 收集生命周期:会判断当前类组件是否声明了生命周期函数:
componentDidUpdate
,getSnapshotBeforeUpdate
,声明了则会将相对应的flag添加到当前fiber节点的flags属性上,在commit阶段调用
finishClassComponent函数
不管更新还是创建,最后都会调用finishClassComponent
函数,这个函数的作用:
- 判断生命周期shouldComponentUpdate返回的值为false的话,则复用之前的fiber节点
- 为true则会调用
render
函数,获取使用最新的state和props生成的React element - 调用
reconcileChildren
函数,这个函数主要作用是进行diff算法,确定需要更新的fiber的最终状态。
function finishClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
shouldUpdate: boolean,
hasContext: boolean,
renderLanes: Lanes,
) {
// 判断是否有ref,有的话则会收集flag到flags上,在comit阶段执行
markRef(current, workInProgress);
...
const didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;
// 判断shouldUpdate为false的话,则复用之前的fiber节点
if (!shouldUpdate && !didCaptureError) {
...
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
const instance = workInProgress.stateNode;
let nextChildren;
...
// 调用组件render方法获取到react element
nextChildren = instance.render();
...
// diff算法
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
...
return workInProgress.child;
}
最后返回子Fiber节点,进行beginWork。reconcileChildren
函数会在写diff算法时单独进行分析。
总结
beginWork做了哪些事情:
- 创建workInProgress树,然后根据current树深度遍历构建workInProgress树
- 会将新旧state和props进行合并更新
- 调用生命周期:
getDerivedStateFromProps,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render
- 收集生命周期,在commit阶段调用:
componentDidmount,componentDidUpdate,getSnapshotBeforeUpdate
- 调用reconcileChildren函数进行diff算法,确定需要更新的fiber的最终状态。