在beginWork中可以看出大多数类型都会调用 reconcileChildren生成Children,并调用reconcileChildFibers生成child fibers并返回workInProgress.child 或 nextUnitOfWork,详细代码请看官网。beginWork是在workLoop中,在此构成的循环栈为workLoop -> performUnitOfWork -> beginWork -> updateWorkTags ->reconcileChildren -> reconcileChildFibers -> workLoop,其中workTags是中work的类型标签。
//packages\react-reconciler\src\ReactChildFiber.js
export const reconcileChildFibers = ChildReconciler(true);//
export const mountChildFibers = ChildReconciler(false);
export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderExpirationTime: ExpirationTime,
) {
if (current === null) {
workInProgress.child = mountChildFibers(workInProgress, null,
nextChildren, renderExpirationTime);
} else {
// init App 走此处,current 为
workInProgress.child = reconcileChildFibers(workInProgress, current.child,
nextChildren,renderExpirationTime);
}
}
reconcileChildren 中的 mountChildFibers 和 reconcileChildFibers 调用的都是 ChildReconciler。ChildReconciler是一个小型的Reconciler。ChildReconciler的代码如下:
function ChildReconciler(shouldTrackSideEffects) {//是否追踪Side-Effects
//....
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
expirationTime: ExpirationTime,
): Fiber | null {
// 这个函数不是递归的。如果顶层项目是一个数组,我们将它视为一组子元素,而不是一个片段.
// 另一方面,嵌套数组将被视为片段节点. 递归在正常的流程中发生.
const isUnkeyedTopLevelFragment =
typeof newChild === 'object' &&
newChild !== null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children;
}
// Handle object types
const isObject = typeof newChild === 'object' && newChild !== null;
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(
reconcileSingleElement(returnFiber,currentFirstChild,newChild,expirationTime)
);
case REACT_PORTAL_TYPE:
return placeSingleChild(
reconcileSinglePortal(returnFiber,currentFirstChild,newChild,expirationTime)
);
}
}
if (typeof newChild === 'string' || typeof newChild === 'number') {
return placeSingleChild(
reconcileSingleTextNode( returnFiber,currentFirstChild,'' + newChild,expirationTime)
);
}
if (isArray(newChild)) {
return reconcileChildrenArray(returnFiber,currentFirstChild,newChild,expirationTime);
}
if (getIteratorFn(newChild)) {
return reconcileChildrenIterator(returnFiber,currentFirstChild,newChild,expirationTime);
}
if (isObject) {
throwOnInvalidObjectType(returnFiber, newChild);
}
if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
//...
}
// Remaining cases are all treated as empty.
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
return reconcileChildFibers;
}
mountChildFibers 和 reconcileChildFibers 的值都是 ChildReconciler 的返回值是reconcileChildFibers。ChildReconciler reconcileChildFibers 生成 workTags 类型的fiber,ChildReconciler reconcileChildFibers执行返回的fiber是workLoop中 nextUnitOfWork。看下执行代码:
function workLoop(isYieldy) {
while (nextUnitOfWork !== null) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
const current = workInProgress.alternate;
next = beginWork(current, workInProgress, nextRenderExpirationTime);
return next;
}
function beginWork(current: Fiber | null, workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): Fiber | null {
return updateWorkTagsComponent(current, workInProgress, Component,
resolvedProps, renderExpirationTime)
}
function updateWorkTagsComponent(current, workInProgress, Component,
resolvedProps, renderExpirationTime){
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
return workInProgress.child
}
workLoop -> reconcileChildren 执行过程中有一些参数身份上的变化:
workInProgress.child 在 workLoop 中是 nextUnitOfWork;
nextUnitOfWork 在performUnitOfWork 是workInProgress;
以App Component 为例,代码如下:
class App extends Component {
render() {
return (<div id='appId'>Hello World!</div>);
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
在 reconcileChildFibers 做断电,调用栈如下:
无论ReactDom.render渲染什么元素,第一个 nextUnitOfWork 是 FiberRoot(类型是HostRoot)。此时执行的代码如下:
function performUnitOfWork(workInProgress) {
var current$$1 = workInProgress.alternate;
next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
workInProgress.memoizedProps = workInProgress.pendingProps;
//...
}
function beginWork(current$$1, workInProgress, renderExpirationTime) {
case HostRoot:
return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
}
function updateHostRoot(current$$1, workInProgress, renderExpirationTime){
var updateQueue = workInProgress.updateQueue;
var nextProps = workInProgress.pendingProps;
var prevState = workInProgress.memoizedState;
processUpdateQueue(workInProgress, updateQueue, nextProps, null, renderExpirationTime);
var nextState = workInProgress.memoizedState;
// being called "element".
var nextChildren = nextState.element;//state中负载element(App ReactElement)
var root = workInProgress.stateNode;
if(...){ //省略 }else{
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
resetHydrationState();
}
return workInProgress.child;
}
function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
//...
workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child,
nextChildren, renderExpirationTime);
//...
}
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
//....
const isObject = typeof newChild === 'object' && newChild !== null;
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(
reconcileSingleElement(returnFiber,currentFirstChild,newChild,expirationTime)
);
}
}
//...
}
从performUnitOfWork 执行到reconcileChildFibers时,变量的名称也发生了变化:
workInProgress 到 reconcileChildFibers中则为 returnFiber;
workInProgress.alternate.child 到 reconcileChildFibers 则为 currentFirstChild,updateHostRoot 时值为null;
nextChildren 到 reconcileChildFibers 中则为newChild;
renderExpirationTime 到 reconcileChildFibers 中则为expirationTime,updateHostRoot时值为1073741823;
在updateHostRoot 中得到 nextChildren,此时nextChildren为:
reconcileType
在ChildReconciler 中 reconcileChildFibers 根据newChild 的类型,执行不同 reconcileType。常用的有三种类型,分别是REACT_ELEMENT_TYPE reconcileSingleElement、text的reconcileSingleTextNode、array的 reconcileChildrenArray。
reconcileSingleElement
由于reconcileChildFibers 的 typeof nextChildren 是’object’类型,同时newChild.$$typeof是 REACT_ELEMENT_TYPE 所以执行reconcileSingleElement。
function reconcileSingleElement(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
element: ReactElement,
expirationTime: ExpirationTime,
): Fiber {
const key = element.key;
let child = currentFirstChild;
while (child !== null) {
if (child.key === key) {
if (child.tag === FragmentFragment ? element.type === REACT_FRAGMENT_TYPE
: child.elementType === element.type
) {
deleteRemainingChildren(returnFiber, child.sibling);//单元素删除sibling??
const existing = useFiber(
child,
element.type === REACT_FRAGMENT_TYPE
? element.props.children
: element.props,
expirationTime,
);
existing.ref = coerceRef(returnFiber, child, element);
existing.return = returnFiber;
return existing;
} else {
deleteRemainingChildren(returnFiber, child);
break;
}
} else {
deleteChild(returnFiber, child);
}
child = child.sibling;
}
if (element.type === REACT_FRAGMENT_TYPE) {
const created = createFiberFromFragment(
element.props.children,returnFiber.mode, expirationTime, element.key
);
created.return = returnFiber;
return created;
} else {
const created = createFiberFromElement(element, returnFiber.mode, expirationTime);
created.ref = coerceRef(returnFiber, currentFirstChild, element);
created.return = returnFiber;
return created;
}
}
有一处代码写的比较有意思,多判断还可以这样写:
if(child.tag === FragmentFragment ? element.type === REACT_FRAGMENT_TYPE
: child.elementType === element.type)
此时currentFirstChild为null,Relement.type为EACT_ELEMENT_TYPE ,所以执行:
const created = createFiberFromElement(element, returnFiber.mode, expirationTime);
created.ref = coerceRef(returnFiber, currentFirstChild, element);
created.return = returnFiber;
return created;
fiber
ReactFiber有一个js文件,具体看官网。fiber其实是一个fiberNode。
可以通过Type 和 Props、reactElemennt、fragment、text等来创建,具体的创建方法有:
- createHostRootFiber
- createFiberFromTypeAndProps
- createFiberFromElement
- createFiberFromFragment
- createFiberFromProfiler
- createFiberFromMode
- createFiberFromSuspense
- createFiberFromText
- createFiberFromHostInstanceForDeletion //元素被移除
- createFiberFromPortal
fiber的结构基本相似,只是WorkTag不同。在App Component 中 使用的是createFiberFromElement.
createFiberFromElement
export function createFiberFromElement(
element: ReactElement, mode: TypeOfMode,expirationTime: ExpirationTime,
): Fiber {
let owner = null;
if (__DEV__) { owner = element._owner;}
const type = element.type;
const key = element.key;
const pendingProps = element.props;
const fiber = createFiberFromTypeAndProps(type,key,pendingProps,owner,mode,expirationTime);
if (__DEV__) {
fiber._debugSource = element._source;
fiber._debugOwner = element._owner;
}
return fiber;
}
element是react element,pendingProps 值为 element.props。
createFiberFromTypeAndProps
export function createFiberFromTypeAndProps(
type: any, // React$ElementType
key: null | string,
pendingProps: any,
owner: null | Fiber,
mode: TypeOfMode,
expirationTime: ExpirationTime,
): Fiber {
let fiber;
let fiberTag = IndeterminateComponent;
// The resolved type is set if we know what the final type will be. I.e. it's not lazy.
let resolvedType = type;
if (typeof type === 'function') {
if (shouldConstruct(type)) {
fiberTag = ClassComponent;
}
} else if (typeof type === 'string') {
fiberTag = HostComponent;
} else {
getTag: switch (type) {
//....
}
}
fiber = createFiber(fiberTag, pendingProps, key, mode);
fiber.elementType = type;
fiber.type = resolvedType;
fiber.expirationTime = expirationTime;
return fiber;
}
在createFiberFromTypeAndProps中经过计算得到fiberTag为ClassComponent。确定fiberTag 后,通过createFiber得到FiberNode。
FiberNode
const createFiber = function(tag,pendingProps,key,mode){
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
return new FiberNode(tag, pendingProps, key, mode);
};
//packages\react-reconciler\src\ReactFiber.js
function FiberNode(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.contextDependencies = null;
this.mode = mode;
// Effects
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.expirationTime = NoWork;
this.childExpirationTime = NoWork;
this.alternate = null;
if (enableProfilerTimer) {
this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}
}
React tree 其实是 fiber 的集合,也是FiberNode的集合。FiberNode与 domNode 基本上是对应的。
placeSingleChild
function placeSingleChild(newFiber: Fiber): Fiber {
if (shouldTrackSideEffects && newFiber.alternate === null) {
newFiber.effectTag = Placement;
}
return newFiber;
}
执行完 reconcileSingleElement 得到 FiberNode,并未添加effect。placeSingleChild 判断新建的fiber是否添加 effect。
next newFiber
执行完placeSingleChild 后就回到了workLoop上,此时 nextUnitOfWork 的值为App Component的fiber,值为:
接下来执行的栈就是:
workLoop -> performUnitOfWork -> beginWork( ClassComponent ) -> updateClassComponent
此时 updateClassComponent 的参数如下:
current 为null;
workInProgress 为 nextUnitOfWork ;
Component 是 workInProgress.type 值为 f App();
nextProps 是 workInProgress.pendingProps 值为 {};
function updateClassComponent(current, workInProgress,Component,
nextProps,renderExpirationTime ) {
const instance = workInProgress.stateNode;
let shouldUpdate;
if (instance === null) {
if (current !== null) {
current.alternate = null;
workInProgress.alternate = null;
workInProgress.effectTag |= Placement;
}
constructClassInstance(workInProgress,Component,nextProps,renderExpirationTime);
mountClassInstance(workInProgress,Component,nextProps,renderExpirationTime);
shouldUpdate = true;
} else if (current === null) { //...} else {//... }
const nextUnitOfWork = finishClassComponent(current,workInProgress,Component,
shouldUpdate,hasContext,renderExpirationTime);
return nextUnitOfWork;
}
updateClassComponent 中的方法已在beginWork演示。此处需要注意的是 finishClassComponent 中的 nextChildren = instance.render(),也就是在此执行 App 中的render函数。在执行 instance.render 时会触发相关的校验,此处不再深入。执行完render得到 nextChildren 为:
接着就是执行 reconcileChildren。reconcileChildren参数current为null,执行mountChildFibers。mountChildFibers的shouldTrackSideEffects 为false,不再追踪effects。mountChildFibers 和 reconcileChildFibers 类似。
function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
if (current$$1 === null) {
workInProgress.child = mountChildFibers(workInProgress, null,
nextChildren, renderExpirationTime);
}
}
reconcileChildFibers中的 newChild.$$typeof 为 REACT_ELEMENT_TYPE,执行的也是reconcileSingleElement,和上面的App Component 类似,得到 nextUnitOfWork 是一个type为”div”的fiber,其结构如下:
tag为5是HostComponent,beginWork中执行 updateHostComponent。在 updateHostComponent 需要注意的是shouldSetTextContent,当 typeof props.children === ‘string’ 则将nextChildren设置为null。
export function shouldSetTextContent(type: string, props: Props): boolean {
return (
type === 'textarea' || type === 'option' || type === 'noscript' ||
typeof props.children === 'string' || typeof props.children === 'number' ||
(typeof props.dangerouslySetInnerHTML === 'object' &&
props.dangerouslySetInnerHTML !== null &&
props.dangerouslySetInnerHTML.__html != null)
);
}
这是处理 HostComponent 较为特殊的点。若此时 typeof props.children !== ‘string’,则workInProgress.child不为null。也就是说 div元素是一个fiber,文本也可以是一个fiber,这个根据children的type而定。
nextChildren为null,照常执行。在 deleteRemainingChildren 中返回null。
此时 beginWork 返回 null,接下来就进入到 complete Work。