react source code analysis 6.legacy mode and concurrent mode
Video Explanation (efficient learning): Click to learn
Course Directory:
1. Introduction and interview questions
3.react source code architecture
4. Source directory structure and debugging
6.legacy and concurrent mode entry functions
20. Summary & answers to interview questions in Chapter 1
react start mode
React has three modes to enter the entry of the main function, which can be accessed from the official document of react Use Concurrent mode (experimental) Three modes are compared:
- legacy mode: reactdom.render (< app / >, RootNode). This is how the React app is currently used. There is no plan to delete this mode, but this mode may not support these new functions.
- blocking mode: reactdom. Createblockingroot (RootNode). Render (< app / >). At present, the experiment is under way. As the first step in migrating to concurrent mode.
- concurrent mode: reactdom.createroot (RootNode). Render (< app / >). At present, in the experiment, it is intended to be the default development mode of React after it is stable in the future. This mode opens all new functions.
Characteristic comparison:
legacy mode has the function of automatic batch processing in composite events, but it is limited to one browser task. Nonstable must be used for non React events to use this function_ batchedUpdates. In blocking mode and concurrent mode, all setstates are batch processed by default. A warning will be issued during development
The meaning of different modes in react runtime
legacy mode is commonly used by us. The process of building dom is synchronous. Therefore, in render's reconciler, if the diff process is particularly time-consuming, the result is that js always blocks high priority tasks (such as user click events), which shows that the page is stuck and cannot respond.
concurrent Mode is the future mode of react. It uses time slice scheduling to realize asynchronous and interruptible tasks. According to different device performance, the length of time slice is also different. In each time slice, if the task reaches the expiration time, it will actively give up the thread to the high priority task. This section will the scheduler & lane model in section 15.
The main execution process of the two mode functions
1. Main execution process:
2. Detailed function calling process:
Use demo_0 is clearer with the video debugging. The yellow part is the main task to create fiberRootNode and rootFiber, the red part is to create Update, and the blue part is the entry function of the scheduling render phase
3.legacy mode:
-
Render calls legacyRenderSubtreeIntoContainer, and finally createRootImpl calls to createFiberRoot to create fiberRootNode, then calls createHostRootFiber to create rootFiber, where fiberRootNode is the root node of the whole project. RootFiber is the node node in the current application, that is, the root node after the ReactDOM.render is invoked.
//The top-level node is the root node of the entire project, fiberRootNode ReactDOM.render(<App />, document.getElementById("root"));//rootFiber ReactDOM.render(<App />, document.getElementById("root"));//rootFiber
-
After creating the Fiber node, legacyRenderSubtreeIntoContainer calls updateContainer to create an Update object and mount it on the ring linked list of updateQueue. Then, execute scheduleUpdateOnFiber and call performSyncWorkOnRoot to enter the render stage and commit stage
4.concurrent mode:
- createRoot calls createRootImpl to create fiberRootNode and rootNode
- After creating the Fiber node, call ReactDOMRoot.prototype.render to execute updateContainer, then scheduleUpdateOnFiber asynchronously dispatches performConcurrentWorkOnRoot into the render stage and commit stage.
5. Comments on main functions of legacy mode
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) { //... var root = container._reactRootContainer; var fiberRoot; if (!root) { // mount time root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);//Create root node fiberRoot = root._internalRoot; if (typeof callback === 'function') {//Process callback var originalCallback = callback; callback = function () { var instance = getPublicRootInstance(fiberRoot); originalCallback.call(instance); }; } unbatchedUpdates(function () { updateContainer(children, fiberRoot, parentComponent, callback);//Create update entry }); } else { // update fiberRoot = root._internalRoot; if (typeof callback === 'function') {//Process callback var _originalCallback = callback; callback = function () { var instance = getPublicRootInstance(fiberRoot); _originalCallback.call(instance); }; } updateContainer(children, fiberRoot, parentComponent, callback); } }
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) { var root = new FiberRootNode(containerInfo, tag, hydrate);//Create fiberRootNode const uninitializedFiber = createHostRootFiber(tag);//Create rootFiber //rootFiber and fiberRootNode connections root.current = uninitializedFiber; uninitializedFiber.stateNode = root; //Create updateQueue initializeUpdateQueue(uninitializedFiber); return root; } //For HostRoot or ClassComponent, an updateQueue is created using initializeUpdateQueue, and then the updateQueue is mounted on the fiber node export function initializeUpdateQueue<State>(fiber: Fiber): void { const queue: UpdateQueue<State> = { baseState: fiber.memoizedState,//The initial state. Based on this state, the new state will be calculated according to Update firstBaseUpdate: null,//The header of the linked list formed by Update lastBaseUpdate: null,//The tail of the linked list formed by Update //The newly generated update will be saved on shared.pending as a one-way circular linked list. When calculating the state, the circular linked list will be cut off and connected to // After lastBaseUpdate shared: { pending: null, }, effects: null, }; fiber.updateQueue = queue; }
function updateContainer(element, container, parentComponent, callback) { var lane = requestUpdateLane(current$1);//Get the currently available lane and explain it in Chapter 12 var update = createUpdate(eventTime, lane); //Create update update.payload = { element: element//jsx }; enqueueUpdate(current$1, update);//update join the team scheduleUpdateOnFiber(current$1, lane, eventTime);//Schedule update return lane; }
function scheduleUpdateOnFiber(fiber, lane, eventTime) { if (lane === SyncLane) {//Synchronous lane corresponding legacy mode //... performSyncWorkOnRoot(root);//The starting point of the render phase is explained in Chapter 6 } else {//concurrent mode //... ensureRootIsScheduled(root, eventTime);//Ensure that root is scheduled } }
6. Notes to concurrent main functions:
function ensureRootIsScheduled(root, currentTime) { //... var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); //Calculate nextLanes //... //Convert the priority of lane to that of Schuller var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority); //Execute performcurrentworkonroot with the priority of schedulerPriorityLevel, which is the starting point of concurrent mode newCallbackNode = scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root)); }
7. Differences between the two modes:
- The second parameter passed in createRootImpl is different. One is LegacyRoot and the other is ConcurrentRoot
- The priority of the lane obtained in requestUpdateLane is different
- In the function scheduleUpdateOnFiber, different branches are entered according to different priorities. The legacy mode enters performSyncWorkOnRoot, and the concurrent mode will asynchronously schedule performcurrentworkonroot