What happens after ReactDOM.render is executed in the react source code?

ReactDOM.render

It is usually used as shown in the figure below to render a React element in the provided container and return a reference to the component (or null for stateless components). This article mainly analyzes the execution process of ReactDOM.render in subsequent articles to analyze the details of creation and update. The source code part of the article __DEV__removes part of the code for the convenience of reading.

ReactDOM.render(<App />,document.getElementById('root')
); 

render

lie in:react-dom/src/client/ReactDOMLegacy.js

export function render( element: React$Element<any>,container: Container,callback: ?Function, ) {// 验证container是否为有效的DOM节点invariant(isValidContainer(container),'Target container is not a DOM element.',);return legacyRenderSubtreeIntoContainer(null,element,container,false,callback,);
} 

A legacyRenderSubtreeIntoContainer function is returned, here note that there are 5 parameters

parentComponent: The parent component is null because it is created for the first time.

children: the incoming ReactElement

container: Render React's DOM container

forceHydrate: Determine whether coordination is required . In the case of server-side rendering, the rendered DOM structure is similar, so it can be reused after comparison. Using ReactDOM.hydrate() in the case of server-side rendering is the same as render() except that forceHydrate will be marked as true.

callback: Callback function after rendering

legacyRenderSubtreeIntoContainer

Located in: react-dom/src/client/ReactDOMLegacy.jsRole:

  • Determine whether it is the first rendering, if so, create root and assign root._internalRoot to fiberRoot and encapsulate the callback callback, and then call unbatchedUpdates to immediately update the child nodes.
  • If it is not the first rendering, enter the normal updateContainer process.
  • Finally, getPublicRootInstance(fiberRoot) returns the public Root instance object.
function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>,children: ReactNodeList,container: Container,forceHydrate: boolean,callback: ?Function, ) {// TODO: Without `any` type, Flow says "Property cannot be accessed on any// member of intersection type." Whyyyyyy.let root: RootType = (container._reactRootContainer: any);let fiberRoot;if (!root) {// Initial mount 初次渲染创建FiberRootroot = container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate,);fiberRoot = root._internalRoot;if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = getPublicRootInstance(fiberRoot);originalCallback.call(instance);};}// Initial mount should not be batched.unbatchedUpdates(() => {updateContainer(children, fiberRoot, parentComponent, callback);});} else {fiberRoot = root._internalRoot;if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = getPublicRootInstance(fiberRoot);originalCallback.call(instance);};}// UpdateupdateContainer(children, fiberRoot, parentComponent, callback);}return getPublicRootInstance(fiberRoot);
} 

legacyCreateRootFromDOMContainer

Location: react-dom/src/client/ReactDOMLegacy.jsThe initial rendering enters the link of root creation: root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate)Function: mainly to judge whether it is server-side rendering, if so, it will reuse the existing dom nodes for reconciliation to improve performance, if not, it will clear the child elements in the container , and finally pass in the container and shouldHydrate to return the createLegacyRoot function.

function legacyCreateRootFromDOMContainer( container: Container,forceHydrate: boolean, ): RootType {const shouldHydrate =forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // 判断是否是服务端渲染// First clear any existing content.if (!shouldHydrate) {let warned = false;let rootSibling;while ((rootSibling = container.lastChild)) {container.removeChild(rootSibling);}}return createLegacyRoot(container,shouldHydrate? {hydrate: true,}: undefined,);
} 

createLegacyRoot

Location: react-dom/src/client/ReactDOMRoot.jsFunction: An instance of ReactDOMBlockingRoot is returned, and the LegacyRoot passed in here is a constant = 0, which represents the synchronous rendering mode currently used, and is for the subsequent Concurrent interruptible rendering mode.

export function createLegacyRoot(container: Container,options?: RootOptions, // hydrate
): RootType {return new ReactDOMBlockingRoot(container, LegacyRoot, options);
} 

ReactDOMBlockingRoot

Located in: react-dom/src/client/ReactDOMRoot.jsFunction: createRootImplMount the return of the function (FiberRoot) to the _internalRoot of the instance

function ReactDOMBlockingRoot( container: Container,tag: RootTag,options: void | RootOptions, ) {this._internalRoot = createRootImpl(container, tag, options);
} 

Related reference video explanation: enter learning

createRootImpl

Location: react-dom/src/client/ReactDOMRoot.jsFunction: Execute createContainer to get FiberRootNode and assign it to root, and then mount RootFiber to the container through markContainerAsRoot.

function createRootImpl( container: Container,tag: RootTag,options: void | RootOptions, ) {// Tag is either LegacyRoot or Concurrent Rootconst hydrate = options != null && options.hydrate === true;const hydrationCallbacks =(options != null && options.hydrationOptions) || null;// 拿到FiberRootNodeconst root = createContainer(container, tag, hydrate, hydrationCallbacks);// 将FiberRootNode挂载到containermarkContainerAsRoot(root.current, container);if (hydrate && tag !== LegacyRoot) {const doc =container.nodeType === DOCUMENT_NODE? container: container.ownerDocument;eagerlyTrapReplayableEvents(container, doc);}return root;
} 

createContainer

Located in: react-reconciler/src/ReactFiberReconciler.old.jsFunction: Return to createFiberRoot

export function createContainer(containerInfo: Container,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): OpaqueRoot {return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
} 

createFiberRoot

Location: react-reconciler/src/react-reconciler/src/ReactFiberReconciler.old.jsFunction: Create a new FiberRoot object and assign it to root, initialize Fiber (usually called RootFiber) to connect the two through root.current = uninitializedFiber and uninitializedFiber.stateNode = root. Execute to initializeUpdateQueue(uninitializedFiber)create an update queue, mount fiber.updateQueue and finally return root

export function createFiberRoot(containerInfo: any,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): FiberRoot {// 新建fiberRoot对象const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);if (enableSuspenseCallback) {root.hydrationCallbacks = hydrationCallbacks;}// Cyclic construction. This cheats the type system right now because// stateNode is any.//初始化RootFiberconst uninitializedFiber = createHostRootFiber(tag);root.current = uninitializedFiber;// RootFiber的stateNode指向FiberRootuninitializedFiber.stateNode = root;initializeUpdateQueue(uninitializedFiber);return root;
} 

FiberRoot RootFiber 和 updateQueue

ReactDOM.render mainly creates three objects FiberRooat, RootFiber and Updatequeue Let's analyze these three objects

FiberRoot

FiberRoot is an instance of FiberRootNode (containerInfo, tag, hydrate) located in: react-reconciler/src/ReactFiberRoot/FiberRootNodefunction:

  • The starting point of the entire application
  • Contains target nodes for application mounts
  • Record various information throughout the application update process
function FiberRootNode(containerInfo, tag, hydrate) {// 标记不同的组件类型this.tag = tag;// 当前应用对应的Fiber对象,是Root Fiber// current:Fiber对象 对应的是 root 节点,即整个应用根对象this.current = null;// root节点,render方法接收的第二个参数this.containerInfo = containerInfo; // 只有在持久更新中会用到,也就是不支持增量更新的平台,react-dom不会用到this.pendingChildren = null;this.pingCache = null;//任务有三种,优先级有高低://(1)没有提交的任务//(2)没有提交的被挂起的任务//(3)没有提交的可能被挂起的任务 //当前更新对应的过期时间this.finishedExpirationTime = NoWork;//已经完成任务的FiberRoot对象,如果你只有一个Root,那么该对象就是这个Root对应的Fiber或null//在commit(提交)阶段只会处理该值对应的任务this.finishedWork = null;// 在任务被挂起的时候通过setTimeout设置的返回内容,用来下一次如果有新的任务挂起时清理还没触发的timeout(例如suspense返回的promise)this.timeoutHandle = noTimeout;// 顶层context对象,只有主动调用renderSubTreeIntoContainer时才会被调用this.context = null;this.pendingContext = null;// 第一次渲染是否需要调和this.hydrate = hydrate;// Node returned by Scheduler.scheduleCallbackthis.callbackNode = null;this.callbackPriority = NoPriority;//存在root中,最旧的挂起时间//不确定是否挂起的状态(所有任务一开始均是该状态)this.firstPendingTime = NoWork;this.firstSuspendedTime = NoWork;this.lastSuspendedTime = NoWork;this.nextKnownPendingLevel = NoWork;//存在root中,最新的挂起时间//不确定是否挂起的状态(所有任务一开始均是该状态)this.lastPingedTime = NoWork;this.lastExpiredTime = NoWork;this.mutableSourcePendingUpdateTime = NoWork;if (enableSchedulerTracing) {this.interactionThreadID = unstable_getThreadID();this.memoizedInteractions = new Set();this.pendingInteractionMap = new Map();}if (enableSuspenseCallback) {this.hydrationCallbacks = null;}
} 

RootFiber

RootFiber is initialized to const uninitializedFiber = createHostRootFiber(tag) 通过 createFiber 返回 FiberNode的实例function:

  • Each ReactElement corresponds to a Fiber object
  • Record the various states of the node (it is convenient for hooks, because the record state and props are mounted to this after the Fiber is completed, for example: pendingProps pendingState memoizedProps memoizedState)
  • Concatenate the entire application to form a tree structure
// 位于 react-reconciler/src/ReactFiber.js
export function createHostRootFiber(tag: RootTag): Fiber {let mode;if (tag === ConcurrentRoot) {mode = ConcurrentMode | BlockingMode | StrictMode;} else if (tag === BlockingRoot) {mode = BlockingMode | StrictMode;} else {mode = NoMode;}return createFiber(HostRoot, null, null, mode);
}

const createFiber = function(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode,
): Fiber {return new FiberNode(tag, pendingProps, key, mode);
};

// FiberNode结构
function FiberNode(tag: WorkTag,pendingProps: mixed,key: null | string,mode: TypeOfMode,
) {// Instance// 标记不同的组件类型this.tag = tag;// ReactElement里面的keythis.key = key;// ReactElement.type,也就是我们调用`createElement`的第一个参数this.elementType = null;// 异步组件lazy component resolved之后返回的内容,一般是`function`或者`class`组件this.type = null;// 对应节点的实例,比如类组件就是class的实例,如果是dom组件就是dom实例,如果是function component就没有实例这里为空this.stateNode = null;// Fiber Fiber是个链表通过child和Sibling连接,遍历的时候先遍历child如果没有子元素了则访问return回到上级查询是否有sibling// 指向他在Fiber节点树中的‘parent’,用来在处理完这个节点之后向上返回this.return = null;// 指向第一个子节点this.child = null;// 指向自己的兄弟节点,兄弟节点的return指向同一个副节点this.sibling = null;this.index = 0;this.ref = null;// 新的变动带来的新的propsthis.pendingProps = pendingProps;// 上次渲染完成后的propsthis.memoizedProps = null;// 该Fiber对应的组件产生的update会存放在这个队列(比如setState和forceUpdate创建的更新)this.updateQueue = null;// 上一次的statethis.memoizedState = null;this.dependencies = null;// this.mode = mode;// Effects// 用来记录副作用this.effectTag = NoEffect;// 单链表用来快速查找下一个side effectthis.nextEffect = null;// 子树中第一个side effectthis.firstEffect = null;// 子树中最后一个side effectthis.lastEffect = null;// 代表任务在未来的哪个时候应该被完成 就是过期时间// 不包括他的子树产生的任务this.expirationTime = NoWork;// 快速确定子树中是否有不再等待的变化this.childExpirationTime = NoWork;// Fiber树更新过程中,每个FIber都会有一个跟其对应的Fiber// 我们称他为`current <==> workInProgress`// 渲染完成后他们会交换位置this.alternate = null;// 调试相关的去掉了

} 

updateQueue

initializeUpdateQueue(uninitializedFiber);Location: react-reconciler/src/ReactUpdateQueue.jsFunction: One-way linked list, used to store update, next to connect update in series. There are many things involved in Update and UpdateQueue, and I plan to explain it in a separate chapter.

export function initializeUpdateQueue<State>(fiber: Fiber): void {const queue: UpdateQueue<State> = {// 每次操作完更新阿之后的statebaseState: fiber.memoizedState,// 队列中的第一个`Update`firstBaseUpdate: null,// 队列中的最后一个`Update`lastBaseUpdate: null,shared: {pending: null,},effects: null,};fiber.updateQueue = queue;
} 

flow chart

Finally, the general flow chart drawn

At last

Organized 75 JS high-frequency interview questions, and gave answers and analysis, which can basically guarantee that you can cope with the interviewer's questions about JS.



Friends in need, you can click the card below to receive and share for free

Guess you like

Origin blog.csdn.net/web22050702/article/details/128640039