beginWork:
验证当前 fiber 树是否需要更新
更新传入的节点类型进行对应的更新
更新后调和子节点
1.第一步验证当前 fiber 树是否需要更新:
// workInProgress.alternate workInProgress下个将要更新的节点 本次优先级最大的节点
function beginWork(current$$1, workInProgress, renderExpirationTime) {
// 本身的过期时间 只有ReactDom.render时才有值
var updateExpirationTime = workInProgress.expirationTime;
//在当前节点 初次 渲染时为null
if (current$$1 !== null) {
var oldProps = current$$1.memoizedProps;
var newProps = workInProgress.pendingProps;
//判断props context是否有修改
if (oldProps !== newProps || hasContextChanged()) {
// If props or context changed, mark the fiber as having performed work.
// This may be unset if the props are determined to be equal later (memo).
didReceiveUpdate = true;
} else if (updateExpirationTime < renderExpirationTime) { //当前更新的优先级是否大于 fiber 节点上优先级最高的节点的优先级
didReceiveUpdate = false;
// This fiber does not have any pending work. Bailout without entering
// the begin phase. There's still some bookkeeping we that needs to be done
// in this optimized path, mostly pushing stuff onto the stack.
...
//bailoutOnAlreadyFinishedWork 会判断这个 fiber 的子树是否需要更新,如果有需要更新会 clone 一份到 workInProgress.child 返回到 workLoop 的 nextUnitOfWork, 否则为 null
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
}
} else {
didReceiveUpdate = false;
}
...
}
bailoutOnAlreadyFinishedWork:
//bailoutOnAlreadyFinishedWork 会判断这个 fiber 的子树是否需要更新,
如果有需要更新会 clone 一份到 workInProgress.child 返回到 workLoop 的 nextUnitOfWork, 否则为 null,代表可以直接complete了
function bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime) {
cancelWorkTimer(workInProgress);
if (current$$1 !== null) {
// Reuse previous context list
workInProgress.contextDependencies = current$$1.contextDependencies;
}
if (enableProfilerTimer) {
// Don't update "base" render times for bailouts.
stopProfilerTimerIfRunning(workInProgress);
}
// Check if the children have any pending work.
var childExpirationTime = workInProgress.childExpirationTime;
if (childExpirationTime < renderExpirationTime) {
// The children don't have any work either. We can skip them.
// TODO: Once we add back resuming, we should check if the children are
// a work-in-progress set. If so, we need to transfer their effects.
return null;
} else {
// This fiber doesn't have work, but its subtree does. Clone the child
// fibers and continue.
cloneChildFibers(current$$1, workInProgress);
return workInProgress.child;
}
}
回到 beginWork :
// 进行更新先把当前 fiber 的 expirationTime 设置为 NoWork
workInProgress.expirationTime = NoWork;
//workInProgress.type: 当函数组件时是 function 当为 class 组件时就是 class 构造函数 当 dom 原生组件时就是标签 div 这种字符串
// pendingProps: 表示新的props
//renderExpirationTime: fiberRoot.expirationTime,fiberRoot 上最大优先级的值
//nextProps: workInProgress.pendingProps,此次更新带来的新 props
switch (workInProgress.tag) {
case IndeterminateComponent:
{
var elementType = workInProgress.elementType;
return mountIndeterminateComponent(current$$1, workInProgress, elementType, renderExpirationTime);
}
case LazyComponent: //lazy组件只有在加载的组件还没完成之前才会被执行
{
var _elementType = workInProgress.elementType;
return mountLazyComponent(current$$1, workInProgress, _elementType, updateExpirationTime, renderExpirationTime);
}
case FunctionComponent://函数 组件
{
var _Component = workInProgress.type;
var unresolvedProps = workInProgress.pendingProps;
var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps);
return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
}
case ClassComponent: //class 组件
{
var _Component2 = workInProgress.type;
var _unresolvedProps = workInProgress.pendingProps;
var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
}
case HostRoot: //
return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
case HostComponent: //html 标签
return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
case HostText: //文本内容
return updateHostText(current$$1, workInProgress);
case SuspenseComponent:
return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
case HostPortal:
return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
case ForwardRef:
{
var type = workInProgress.type;
var _unresolvedProps2 = workInProgress.pendingProps;
var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
return updateForwardRef(current$$1, workInProgress, type, _resolvedProps2, renderExpirationTime);
}
case Fragment:
return updateFragment(current$$1, workInProgress, renderExpirationTime);
case Mode:
return updateMode(current$$1, workInProgress, renderExpirationTime);
case Profiler:
return updateProfiler(current$$1, workInProgress, renderExpirationTime);
case ContextProvider:
return updateContextProvider(current$$1, workInProgress, renderExpirationTime);
case ContextConsumer:
return updateContextConsumer(current$$1, workInProgress, renderExpirationTime);
case MemoComponent:
{
var _type2 = workInProgress.type;
var _unresolvedProps3 = workInProgress.pendingProps;
// Resolve outer props first, then resolve inner props.
var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
{
if (workInProgress.type !== workInProgress.elementType) {
var outerPropTypes = _type2.propTypes;
if (outerPropTypes) {
checkPropTypes(outerPropTypes, _resolvedProps3, // Resolved for outer only
'prop', getComponentName(_type2), getCurrentFiberStackInDev);
}
}
}
_resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
return updateMemoComponent(current$$1, workInProgress, _type2, _resolvedProps3, updateExpirationTime, renderExpirationTime);
}
case SimpleMemoComponent:
{
return updateSimpleMemoComponent(current$$1, workInProgress, workInProgress.type, workInProgress.pendingProps, updateExpirationTime, renderExpirationTime);
}
case IncompleteClassComponent:
{
var _Component3 = workInProgress.type;
var _unresolvedProps4 = workInProgress.pendingProps;
var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4);
return mountIncompleteClassComponent(current$$1, workInProgress, _Component3, _resolvedProps4, renderExpirationTime);
}
case DehydratedSuspenseComponent:
{
if (enableSuspenseServerRenderer) {
return updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
}
break;
}
}
1)mountIndeterminateComponent:
不确定的组件类型
IndeterminateComponent只有在组件被第一次渲染的情况下才会出现,在经过第一次渲染之后,
我们就会更新组件的类型,也就是Fiber.tag。如果出现了_current存在的情况,那么可能是因为渲染时有Suspend的情况
//对于ClassComponent和FunctionalComponent的判断条件
if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
workInProgress.tag = ClassComponent;
...
} else {
workInProgress.tag = FunctionComponent;
reconcileChildren(null, workInProgress, value, renderExpirationTime);
return workInProgress.child;
}
2)mountLazyComponent:
lazy组件只有在加载的组件还没完成之前才会被执行,等到加载完成了,那么就变成加载之后的组件了,永远不会执行到自己的更新
//把lazy组件的Fiber对象的tag和type都改变了,下次执行到这个Fiber的更新,则会直接更新返回的type
workInProgress.type = Component;
var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
3)updateFunctionalComponent:
functionalComponent本身就是一个pure function,所以处理起来相对很简单,获得对应的props和context之后,直接调用就能获得nextChildren,然后就是常规操作reconcileChildren和memoizeProps。
4)updateClassComponent:
其中 constructClassInstance 有 var instance = new ctor(props, context); (所以自定义的class组件引入时候不需要手动new),执行 adoptClassInstance 这个方法给 ClassComponent 的实例挂载一个updater对象:
{
enqueueForceUpdate: func,
enqueueReplaceState: func,
enqueueSetState: func,
isMounted: func
}
function adoptClassInstance(workInProgress, instance) {
instance.updater = classComponentUpdater
workInProgress.stateNode = instance
// The instance needs access to the fiber so that it can schedule updates
set(instance, workInProgress)
}
function set(key, value) {
key._reactInternalFiber = value
}
这里通过set给 instance 的_reactInternalFiber挂载上workInProgress,所以我们可以通过this._reactInternalFiber获取该实例对应的Fiber对象。
接下来执行 mountClassInstance:
初始化 props、state 等实例属性,如果有updateQueue就更新之,一般来说第一次渲染是没有的。
processUpdateQueue用来计算新的state
var getDerivedStateFromProps = workInProgress.type.getDerivedStateFromProps
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(workInProgress, getDerivedStateFromProps, props)
instance.state = workInProgress.memoizedState
}
如果 class 有声明getDerivedStateFromProps,就调用他,调用该方法之后会根据结果更新workInProgress.memoizedState,所以要重新赋值给instance.state
接下去判断一下是否没有新的生命周期方法,如果没有并且有componentWillMount生命周期方法,则调用他。
最后判断是否有componentDidMount,如果有就修改workInProgress.effectTag |= Update,因为componentDidMount要在真正渲染进DOM之后才调用,也就是commit之后。
5)updateHostRoot:
//会判断这个 fiber 的子树是否需要更新如果有需要更新会 clone 一份到 workInProgress.child 返回到 workLoop 的 nextUnitOfWork, 否则为 null
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
processUpdateQueue:
//计算更新 state
function processUpdateQueue(workInProgress, queue, props, instance, renderExpirationTime) {
hasForceUpdate = false;
//克隆一份 updateQueue进行更新
queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);
{
currentlyProcessingQueue = queue;
}
// These values may change as we process the queue.
var newBaseState = queue.baseState; //每次执行完 processUpdateQueue 都会把新的 state 赋值到 queue.baseState
var newFirstUpdate = null;
var newExpirationTime = NoWork;
// Iterate through the list of updates to compute the result.
var update = queue.firstUpdate; //链表起始
var resultState = newBaseState;
while (update !== null) { //对每个update 执行 setState
var updateExpirationTime = update.expirationTime;
//低优先级
if (updateExpirationTime < renderExpirationTime) {
// This update does not have sufficient priority. Skip it.
if (newFirstUpdate === null) {
// This is the first skipped update. It will be the first update in
// the new list.
newFirstUpdate = update;
// Since this is the first update that was skipped, the current result
// is the new base state.
newBaseState = resultState;
}
// Since this update will remain in the list, update the remaining
// expiration time.
//等待下次更新
if (newExpirationTime < updateExpirationTime) {
newExpirationTime = updateExpirationTime;
}
} else {
// This update does have sufficient priority. Process it and compute
// a new result. 计算新的state结果
resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
//setState中第二个参数 callback
var _callback = update.callback;
if (_callback !== null) {
workInProgress.effectTag |= Callback;
// Set this to null, in case it was mutated during an aborted render.
update.nextEffect = null;
if (queue.lastEffect === null) {
queue.firstEffect = queue.lastEffect = update;
} else {
queue.lastEffect.nextEffect = update;
queue.lastEffect = update;
}
}
}
// Continue to the next update. 下个update
update = update.next;
}
...
}
getStateFromUpdate:
根据 update.tag 计算 state 的结果,会判断 setState 传入的函数或对象两种情况
1 函数时会指定上下文,传入 prevState, nextProps
2 对象时就是最终要更新的 state 对象
3 最后通过 Object.assign 生成新的 state 对象
case UpdateState:
{
var _payload2 = update.payload;
var partialState = void 0;
if (typeof _payload2 === 'function') {
// Updater function
partialState = _payload2.call(instance, prevState, nextProps);
{
exitDisallowedContextReadInDEV();
}
} else {
// setState传入的是对象 直接返回
partialState = _payload2;
}
if (partialState === null || partialState === undefined) {
// Null and undefined are treated as no-ops.
return prevState;
}
// _assign合并 state
return _assign({}, prevState, partialState);
}
6)updateHostComponent:
更新 HostComponent 原生 dom 标签
原生标签 小写的 标签
判断标签内容是不是纯文本
是纯文本没子节点,不是纯文本根据之前的 props 标记更新
跟 classCompnent 一样有 makeRef 能使用 ref
接下篇…