React15’s rendering and diff will recursively compare the vDom tree to find the nodes that have been changed (added, deleted, or changed) and then update them synchronously. If the number of nodes on the page is too large, React will constantly tie up browser resources, resulting in unresponsive user actions. Here’s how Time management master Fiber helps React balance business (browser tasks) and love (trigger responses).
What is the Fiber
Fiber is not a new term in computer terminology. Its Chinese translation is called “fiber”, which is the same execution process as Progress, Thread and Coroutine. React Fiber is an update mechanism within React. Supports different priorities of tasks, interrupts and recovers, and reuses the saved state after recovery. Each work update unit is the Fiber node corresponding to the React Element.
React Fiber’s core interaction with the browser
The page is rendered in frames. In general, the refresh rate of the device is 1s 60 times, that is, the number of frames drawn per second (FPS) is less than 60, the page will appear obvious lag, so the drawing time of a frame should not exceed 16.7ms (this time is very important).
Scheduler is added to The Scheduler list in Update 16 compared to Update 15.
First, React requests scheduling from the browser. If the browser has free time within a frame (16.7ms), the browser will determine whether there is any task to be executed. If there is any task to be executed, the browser will determine whether there is still time until the browser completes the task. Fiber is a data structure (stack frame) and a solution for interruptible invocation tasks. Its features are time slicing and Supense.
Updates go from being recursive to a cycle that can be interrupted. Each time the loop calls shouldYield to see if there is any time left.
/ / the react source packages/react - the reconciler/SRC/ReactFiberWorkLoop. New. Js
function workLoopConcurrent() {
// Perform work until Scheduler asks us to yield
// Listen to the scheduler, he said to execute, execute
while(workInProgress ! = =null&&! shouldYield()) { workInProgress = performUnitOfWork(workInProgress); }}Copy the code
Scheduler
We need a mechanism to tell us if the browser has time. RequestIdleCallback is the key API for implementing this mechanism. It allows users to respond quickly to their operations without blocking their interactions. Surprisingly, it does not reduce computation, but only takes the time to fragmentation to the extreme. React abandoned it and implemented the more fully featured requestIdleCallback Polyfill itself, which is called Scheduler.
Scheduler is a library independent of React
The structure of the Fiber
/ / the react source packages/react - the reconciler/SRC/ReactInternalTypes
{
type: any, // For class components, it points to the constructor; For DOM elements, it specifies the HTML tag
key: null | string, // Unique identifier
stateNode: any, // Save references to component class instances, DOM nodes, or other React element types associated with fiber nodes
child: Fiber | null./ / the eldest son
sibling: Fiber | null.// Next brother
return: Fiber | null./ / the parent node
tag: WorkTag, / / define the type of fiber operation, see https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactWorkTags.js
nextEffect: Fiber | null.// Pointer to the next node
updateQueue: mixed, // Queue for status updates, callback functions, AND DOM updates
memoizedState: any, // Create the fiber state for the output
pendingProps: any, // Has been updated from new data in the React element and needs to be applied to props for child components or DOM elements
memoizedProps: any, // Props used to create output during the previous render
/ /...
}
Copy the code
-
type & key
Fiber’s type and key act the same way as React elements. Fiber’s type describes its corresponding component; for composite components, type is the function or class component itself. For native tags (div, SPAN, etc.), type is a string. With different types, key is used during reconciliation to determine whether Fiber can be used again.
-
stateNode
StateNode holds references to component class instances, DOM nodes, or other React element types associated with fiber nodes. In general, you can think of this property as holding local state related to fiber.
-
child & sibling & return
The child property points to the first child of this node. The sibling attribute points to the next sibling of this node (the older son points to the second son, and the second son points to the third son). The return property refers to the parent node of this node, to whom the current node should submit its work once it has finished processing. If a fiber has multiple subfibers, the return fiber of each subfiber is parent.
I’m curious why the parent pointer is called return instead of parent. The child Fiber node and its sibling nodes return to their parent node when they complete their work.
Fiber traversal process
If the emperor died, the throne would pass to his eldest son. If the emperor had no heirs, the throne would pass to his brother. If there was no direct blood line, the throne would pass to his uncle until the dragon veins dried up and the dynasty ended.
The execution sequence is A1 B1 C1 C2 B2 C3 C4
We write this depth-first traversal by hand according to this rule
// see the source code can also learn algorithm 🐶
// iterate over the function
const performUnitOfWork = (Fiber) = > {
// The crown prince is the first in line
if (Fiber.child) {
return Fiber.child
}
while (Fiber) {
// The second line of succession
if (Fiber.sibling) {
return Fiber.sibling
}
// This branch is cut off, go back to choose uncle
Fiber = Fiber.return
}
}
const workloop = (nextUnitOfWork) = > {
// If the execution unit is to be executed, the next execution unit is returned
while (nextUnitOfWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
}
if(! nextUnitOfWork) {console.log('End of Reconciliation Stage')
}
}
workloop(rootFiber)
// rootFiber is constructed in the back while writing
Copy the code
Native debug source code
Let’s take a look at printing a Fiber node in an actual project
We’ll still use create-react-app to create a project. Debugging the source code requires some configuration in the project
- NPM Run eject(exposes webPack configuration)
- Add the React source folder to the SRC file
- Change the react reference path in webpack
I won’t go into details about how to debug the React source code locally, or clone it using my configured project.
This allows us to debug the source code locally
// packages/react-dom/src/ReactDOMLegacy.js
function legacyRenderSubtreeIntoContainer(parentComponent: ? React$Component<any, any>, children: ReactNodeList, container: Container, forceHydrate: boolean, callback: ?Function.) {
if (__DEV__) {
topLevelUpdateWarnings(container);
warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
}
// 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 Initial render
root = 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.
// First render is non-batch update, which can ensure update efficiency and user experience
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);
};
}
// Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
console.log('fiber-----current', fiberRoot.current); // Here we print the fiber tree
return getPublicRootInstance(fiberRoot);
}
Copy the code
// We write JSX like this, div has an H1 tag and an A tag
const jsx = (
<div className="content">I am a<a href="www.baidu.com">Ming is not</a>
</div>
)
Copy the code
Take a look at the print
Let’s draw a linked list diagram of fiber
Let’s write a fiber by hand
We can use the React source code to parse (a) the handwritten render function code
Here we only focus on fiber’s type, props, stateNode, child, Sibling, return attributes. // Fiber js object.
// We construct a fiber node in the render function
let wipRoot = null;
function render(vnode, container) {
wipRoot = {
type: "div".props: {
children: {... vnode}, },stateNode: container,
};
nextUnitOfWOrk = wipRoot;
}
// The native label node receives the fiber node where work is executing
function updateHostComponent(workInProgress) {
const {type, props} = workInProgress;
// Consider whether a node already exists before inserting it
if(! workInProgress.stateNode) { workInProgress.stateNode = createNode(workInProgress); } reconcileChildren(workInProgress, workInProgress.props.children);console.log("workInProgress", workInProgress);
}
// Coordinate child nodes
function reconcileChildren(workInProgress, children) {
if (typeof children === "string" || typeof children === "number") {
return;
}
// wrap an element after React16, or an array
const newChildren = Array.isArray(children) ? children : [children];
// The last fiber node
let previousNewFiber = null;
for (let i = 0; i < newChildren.length; i++) {
let child = newChildren[i];
// Construct a new fiber node
let newFiber = {
type: child.type,
props: {... child.props},stateNode: null.child: null.sibling: null.return: workInProgress,
};
if (i === 0) {
// The first child fiber, the first son
workInProgress.child = newFiber;
} else {
// No to brother
previousNewFiber.sibling = newFiber;
}
// Record a fiberpreviousNewFiber = newFiber; }}function performUnitOfWork(workInProgress) {
// step1 execute the task
// todo
const {type} = workInProgress;
if (typeof type === "string") {
// Native label node
updateHostComponent(workInProgress);
}
// step2 and return to the next execution task
if (workInProgress.child) {
return workInProgress.child;
}
let nextFiber = workInProgress;
while (nextFiber) {
if (nextFiber.sibling) {
returnnextFiber.sibling; } nextFiber = nextFiber.return; }}function workLoop(IdleDeadline) {
while (nextUnitOfWOrk && IdleDeadline.timeRemaining() > 1) {
// Execute the task and return to the next execution task
nextUnitOfWOrk = performUnitOfWork(nextUnitOfWOrk);
}
/ / submit
if (!nextUnitOfWOrk && wipRoot) {
commitRoot();
}
}
// Implement time slice
requestIdleCallback(workLoop);
// End of scheduling, render
function commitRoot() {
commitWorker(wipRoot.child);
wipRoot = null;
}
// Submit the work execution result
function commitWorker(workInProgress) {
// Commit yourself
if(! workInProgress) {return;
}
let parentNodeFiber = workInProgress.return;
let parentNode = parentNodeFiber.stateNode;
if (workInProgress.stateNode) {
parentNode.appendChild(workInProgress.stateNode);
}
// Submit the child node
commitWorker(workInProgress.child);
// Submit sibling nodes
commitWorker(workInProgress.sibling);
}
Copy the code
The effect of
The code has been uploaded to the Git code address
When mounting the components, the Reconciler generates the corresponding Fiber nodes for the components according to the contents of the components described by THE JSX. A DOM tree is constructed using depth first traversal.
conclusion
The Fiber architecture is the cornerstone of React, which uses time slicing to transform synchronous updates into interruptible asynchronous updates. This time, we learned about the interaction flow between Fiber and the browser, and implemented a simple Fiber tree. The traversal flow is traversal first based on depth. But what is not covered here is the prioritization mechanism, how to continue breakpoints, and how to collect task results. Future articles will examine these mechanisms and see how React is updated by diff.
Refer to the link
React Technology Reveals
React Technology revealed
Fully understand React Fiber
Introduction to the React Fiber
React Fiber
Step into the React Fiber world