在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.jsexport 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 typesconst 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$ElementTypekey: 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 constructorsreturn new FiberNode(tag, pendingProps, key, mode);};//packages\react-reconciler\src\ReactFiber.jsfunction FiberNode(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode) {// Instancethis.tag = tag;this.key = key;this.elementType = null;this.type = null;this.stateNode = null;// Fiberthis.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;// Effectsthis.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。
