Preface
This article introduces the following concepts step by step. Following the principles of this article, you can basically understand why fiber is needed, why commit, phases, reconciliation stages and other principles. This article is not exactly consistent with the original text. I will add some of my own thoughts here, such asperformUnitOfWork
After processing, the connection between fiber tree and element tree, etc.
- createElement function
- render function
- Concurrent Mode
- Fibers
- Render and Commit Phases
- Reconciliation
- Function Components
- Hooks
Chapter 1 Basic Concept
The following code is an example
// Syntax is not a legal js syntax// const element = <h1 title="foo">Hello</h1> // 2. The way to convert jsx to js through babel and other compilation tools, and convert jsx to createElement function call// const element = ( // "h1", // { title: "foo" }, // "Hello" // ) // The final object returned is roughly as follows:const element = { type: "h1", props: { title: "foo", children: "Hello", }, } const container = ("root") // (element, container) // 4. Replace the logic of the function, roughly handle the logic:const node = () node['title'] = const text = ("") text["nodeValue"] = (text) (node)
To avoid ambiguity, hereelement
expressReact elements
,node
Represents the real DOM element node.
At this point, this code can run on the browser without any compilation. If you don't believe it, you can copy it to the browser console to try it.
Here are a few points to note:
- Pass first
(text)
Add child elements to parent element and then pass(node)
Add parent element to containercontainer
Triggers the browser rendering page. This order cannot be reversed, which means that only if the entire real dom tree is built, it can be added to the container. Assume that the order is reversed, for example, execute first(node)
, triggers browser reflow. Execute again(text)
It triggers browser reflow. Extremely poor performance -
The final object returned is
virtual dom
Tree,According to this
virtual dom
Create a real dom tree
Chapter 2 createElement function
The following code is an example
The received children may be an atomic value, such as a string or a number,
('h1', {title: 'foo'}, 'Hello')
. To simplify our code, create a specialTEXT_ELEMENT
Type converts it to an object
Refer to the actual video explanation of React:Enter study
= (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } } // const element = ( // <div > // <a>bar</a> // <b /> // </div> // ) // Convert jsx to js syntaxconst element = ( "div", { id: "foo" }, ("a", null, "bar"), ("b") ) const container = ("root") (element, container)
OK, now we have implemented a simple onecreateElement
Function, we can tell babel to use our own when converting jsx to jscreateElement
function:
const MiniReact = { createElement: (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } } } /** @jsx */ const element = ( <div > <a>bar</a> <b /> </div> ) ('element======', element) const container = ("root") (element, container)
Chapter 3 render function
import React from 'react'; function render(element, container) { const dom = === 'TEXT_ELEMENT' ? ("") : () const isProperty = key => key !== 'children' () .filter(isProperty) .forEach(name => { dom[name] = [name] }) (child => { render(child, dom) }); (dom) } const MiniReact = { createElement: (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } }, render } /** @jsx */ const element = ( <div > <a>bar</a> <b /> </div> ) ('element======', element) const container = ("root") (element, container)
render
The function recursively creates the real dom element, then appends each element to its parent element, and finally appends the entire dom tree to the root container, and rendering is completed. Once this process begins, it cannot be interrupted in the middle until the entire application rendering is completed. This is alsoReact16
Rendering process before version
Note that the page will only be displayed when the entire dom tree is append to the root container.
Chapter 4 Concurrent Mode
You can see in Chapter 3 that the current version ofrender
The function recursively builds the dom tree, and finally appends to the root container, and the final page is rendered. There is a problem here. If the number of dom nodes is huge and the recursive level is too deep, this process is actually very time-consuming, which leads torender
The function occupies the main thread for a long time, and the browser cannot respond to user input and other events, causing lag.
Therefore we need torender
The process is split into small task units, and the browser is allowed to interrupt each time one unit is executed.render
Process and execute high-priority tasks, and continue to execute them when the browser is freerender
process
If it is correctrequestIdleCallback
If you are not familiar with it, you can learn about it yourself. This API is not used in real React code because there are compatibility issues. Therefore React usesscheduler package
Simulate this scheduling process
let nextUnitOfWork = null function workLoop(deadline) { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork( nextUnitOfWork ) shouldYield = () < 1 } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) function performUnitOfWork(nextUnitOfWork) { // TODO }
performUnitOfWork
Receive the current unit of work and return to the next unit of work. The work unit can be understood as a fiber object node
workLoop
The loop will be calledperformUnitOfWork
, the loop terminates until all work units have been processed, or the current frame browser has no idle time. Wait until the browser is free next time before continuing to execute
Therefore, we need a data structure that can support task interruption and can continue execution. Obviously, linked lists are very suitable for
Chapter 5 Fibers
Fibers is a data structure that supports splitting the rendering process into work units, which is essentially a two-way linked list. The advantage of this data structure is that it is easy to find the next unit of work
Fiber contains three meanings:
- As an architecture,
React 15
ofReconciler
Execute in recursive way, and the data is stored in the recursive call stack, so it is calledstack Reconciler
。React 16
ofReconciler
Based on Fiber node implementation, it is calledFiber Reconciler
- As a static data structure, each Fiber node corresponds to a
React Element
, saves the type of the component (function component/class component/html tag), corresponding DOM node information, etc. - As a dynamic unit of work, each Fiber node saves the status of the component changing, the work to be performed in this update, etc.
Some cold knowledge about Fiber:
- A Fiber node corresponds to a React Element node, and is also a unit of work
- Each fiber node has a pointer to the first child element, the next brother element, and the parent element**
The following code is an example:
( <div> <h1> <p /> <a /> </h1> <h2 /> </div>, container )
The corresponding fiber tree is as follows:
import React from 'react'; // Create real dom nodes based on fiber nodesfunction createDom(fiber) { const dom = === 'TEXT_ELEMENT' ? ("") : () const isProperty = key => key !== 'children' () .filter(isProperty) .forEach(name => { dom[name] = [name] }) return dom } let nextUnitOfWork = null // The main logic of the render function:// Create root fiber based on root container// Point nextUnitOfWork pointer to root fiber// element is react element treefunction render(element, container){ nextUnitOfWork = { dom: container, props: { children: [element], // At this time, the element is just a virtual dom tree created by the function. }, } } function workLoop(deadline) { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork) shouldYield = () < 1 } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) // The main logic of performUnitOfWork function:// Add element element to DOM// Create corresponding fiber node for element child elements// Return to the next unit of work, that is, the next fiber node, the search process:// 1. If there is a child element, return the fiber node of the child element// 2. If there are no child elements, return the fiber node of the sibling element// 3. If there are neither child elements nor sibling elements, then look up the fiber node of the sibling element of its parent node// 4. If you find the root fiber node upward, it means that the render process has endedfunction performUnitOfWork(fiber) { // The first step is to create a real dom node based on the fiber node and save it in the properties if(!){ = createDom(fiber) } // Step 2 Add the real dom of the current fiber node to the parent node. Note that this step will trigger the browser to reflow and repaint! ! ! if(){ () } // Step 3 Create the corresponding fiber node for the child element const children = let prevSibling ((child, index) => { const newFiber = { type: , props: , parent: fiber, dom: null } if(index === 0){ = newFiber } else { = newFiber } prevSibling = newFiber }) // Step 4: Find the next unit of work if(){ return } let nextFiber = fiber while(nextFiber){ if(){ return } nextFiber = } } const MiniReact = { createElement: (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } }, render } /** @jsx */ const element = ( <div> <h1> <p /> <a /> </h1> <h2 /> </div> ) // const element = ( // <div > // <a>bar</a> // <b /> // </div> // ) ('element======', element) const container = ("root") (element, container)
Here is something worth savoring.Returned
element tree
andperformUnitOfWork
Createdfiber tree
What are the connections? As shown in the figure below:
-
React Element Tree
Is it fromTree structure object created by method
-
Fiber Tree
It is based onReact Element Tree
The tree created. Each Fiber node saves the real DOM node and saves the rightReact Element Tree
CorrespondingElement
Application of nodes. Notice,Element
Nodes will not be savedFiber
Application of nodes
Chapter 6 Render and Commit Phases
Chapter 5performUnitOfWork
There are some problems. In the second step, we directly mount the newly created real dom node to the container, which will bring two problems:
- Each execution
performUnitOfWork
It will cause the browser to reflow and repaint, because the real dom has been added to the browser, and the performance is extremely poor. - The browser can interrupt the rendering process, which may cause users to see an incomplete UI interface.
We need to revise our code,performUnitOfWork
The real dom node is not mounted to the container in the stage. Save reference to the fiber tree root node. Wait until the fiber tree build is completed, the real dom node is submitted once and added to the container.
import React from 'react'; function createDom(fiber) { const dom = === 'TEXT_ELEMENT' ? ("") : () const isProperty = key => key !== 'children' () .filter(isProperty) .forEach(name => { dom[name] = [name] }) return dom } let nextUnitOfWork = null let wipRoot = null function render(element, container){ wipRoot = { dom: container, props: { children: [element], // At this time, the element is just a virtual dom tree created by the function. }, } nextUnitOfWork = wipRoot } function commitRoot(){ commitWork() wipRoot = null } function commitWork(fiber){ if(!fiber){ return } const domParent = ; () commitWork() commitWork() } function workLoop(deadline) { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork) shouldYield = () < 1 } if(!nextUnitOfWork && wipRoot){ commitRoot() } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) function performUnitOfWork(fiber) { // The first step is to create a real dom node based on the fiber node and save it in the properties if(!){ = createDom(fiber) } // Step 2 Add the real dom of the current fiber node to the parent node. Note that this step will trigger the browser to reflow and repaint! ! ! // if(){ // () // } // Step 3 Create the corresponding fiber node for the child element const children = let prevSibling ((child, index) => { const newFiber = { type: , props: , parent: fiber, dom: null } if(index === 0){ = newFiber } else { = newFiber } prevSibling = newFiber }) // Step 4: Find the next unit of work if(){ return } let nextFiber = fiber while(nextFiber){ if(){ return } nextFiber = } } const MiniReact = { createElement: (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } }, render } /** @jsx */ const element = ( <div> <h1> <p /> <a /> </h1> <h2 /> </div> ) // const element = ( // <div > // <a>bar</a> // <b /> // </div> // ) ('element======', element) const container = ("root") (element, container)
Chapter 7 Reconciliation
So far, we have only considered the single scenario of adding dom nodes to containers, and update and deletion have not been implemented yet.
We need to compare the latest onesReact Element Tree
And the latest oneFiber Tree
The difference
We need to add an alternate attribute to each fiber node to save the old fiber node
The old fiber node saved by alternate has the following main uses:
- Reuse real dom nodes on old fiber nodes
- Comparison of props on old fiber nodes and props on new element nodes
- The old fiber node has updated queues saved, and these queues are executed when creating new fiber nodes to get the latest page
const children = reconcileChildren(fiber, children) function reconcileChildren(wipFiber, elements) { let index = 0 let oldFiber = && let prevSibling = null while (index < || oldFiber != null) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && == if (sameType) { newFiber = { type: , props: , dom: , parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } if (element && !sameType) { newFiber = { type: , props: , dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { = "DELETION" (oldFiber) } if (oldFiber) { oldFiber = } if (index === 0) { = newFiber } else if (element) { = newFiber } prevSibling = newFiber index++ } }
As shown in the above code:
Coordination process:
- In essence, it is still creating new ones based on the new React Element Tree
Fiber Tree
, However, in order to save memory overhead, the coordination process will determine whether the new fiber node can reuse the real dom elements on the old fiber node. If it can be reused, there is no need to recreate the real dom elements from beginning to end. At the same time, each new fiber node will also store a reference to the old fiber node, which is convenient for comparing the new and old properties props in the commit stage. - if
old
andnew
The same is true, the old dom node is retained and only the props attribute is updated. - if
type
Different and havenew element
, then create a new real dom node - if
type
Different and haveold fiber
node, delete the real dom node corresponding to the node - To delete a node, you need to have a special array to collect the old fiber nodes that need to be deleted. Since the new fiber tree created by the new element tree does not have the corresponding dom, the old fiber nodes need to be collected and deleted during the commit stage
Note that the coordination process is based on the latest React Element Tree to create a new fiber tree, but the new fiber node reuses the real dom elements of the old fiber node. After all, frequent creation of real doms is very memory-consuming. The new fiber node will still save references to the old fiber node, which is convenient for comparing new attributes and old attributes in the commit stage. There will be a question here. If the new fiber node retains references to the old fiber node, then as the number of updates increases, will the old fiber tree also increase and how to destroy it?
import React from 'react'; function createDom(fiber) { const dom = === 'TEXT_ELEMENT' ? ("") : () updateDom(dom, {}, ) return dom } let nextUnitOfWork = null let wipRoot = null // Save a reference to root fiberlet currentRoot = null // Save the fiber tree corresponding to the current pagelet deletions = null function render(element, container){ wipRoot = { dom: container, props: { children: [element], // At this time, the element is just a virtual dom tree created by the function. }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot } function commitRoot(){ (commitWork) commitWork() currentRoot = wipRoot wipRoot = null } const isEvent = key => ("on") const isProperty = key => key !== "children" && !isEvent(key) const isNew = (prev, next) => key => prev[key] !== next[key] const isGone = (prev, next) => key => !(key in next) function updateDom(dom, prevProps, nextProps) { //Remove old or changed event listeners (prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) ( eventType, prevProps[name] ) }) // Remove old properties (prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // Set new or changed properties (nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // Add event listeners (nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) ( eventType, nextProps[name] ) }) } function commitWork(fiber){ if(!fiber){ return } const domParent = ; if ( === "PLACEMENT" && != null) { () } else if ( === "UPDATE" && != null) { updateDom( , , ) } else if ( === "DELETION") { () } commitWork() commitWork() } function workLoop(deadline) { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork) shouldYield = () < 1 } if(!nextUnitOfWork && wipRoot){ commitRoot() } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) function reconcileChildren(wipFiber, elements) { let index = 0 let oldFiber = && let prevSibling = null while (index < || oldFiber != null) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && == if (sameType) { newFiber = { type: , props: , dom: , parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } if (element && !sameType) { newFiber = { type: , props: , dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { = "DELETION" (oldFiber) } if (oldFiber) { oldFiber = } if (index === 0) { = newFiber } else if (element) { = newFiber } prevSibling = newFiber index++ } } function performUnitOfWork(fiber) { // The first step is to create a real dom node based on the fiber node and save it in the properties if(!){ = createDom(fiber) } // Step 2 Add the real dom of the current fiber node to the parent node. Note that this step will trigger the browser to reflow and repaint! ! ! // if(){ // () // } // Step 3 Create the corresponding fiber node for the child element const children = // let prevSibling // ((child, index) => { // const newFiber = { // type: , // props: , // parent: fiber, // dom: null // } // if(index === 0){ // = newFiber // } else { // = newFiber // } // prevSibling = newFiber // }) reconcileChildren(fiber, children) // Step 4: Find the next unit of work if(){ return } let nextFiber = fiber while(nextFiber){ if(){ return } nextFiber = } } const MiniReact = { createElement: (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } }, render } /** @jsx */ const container = ("root") const updateValue = e => { rerender() } const rerender = value => { const element = ( <div> <input onInput={updateValue} value={value} /> <h2>Hello {value}</h2> </div> ) (element, container) } rerender("World")
Chapter 8 Function Components
The following code in this chapter is an example:
/** @jsx */ const container = ("root") function App(props){ return <h1>Hi { }</h1> } const element = <App name="foo" /> (element, container)
After jsx is compiled by babel:
function App(props) { return ("h1", null, "Hi ", ); } const element = (App, { name: "foo" });
There are two differences between function components:
- The fiber node corresponding to the function component does not have a corresponding real dom element.
- You need to execute a function to get the corresponding children node, rather than directly from
Get
Since the function component does not have a corresponding fiber node, when looking for the dom corresponding to the parent fiber node during the commit stage, it is necessary to determine whether the dom element exists.
The complete code of this chapter:
import React from 'react'; function createDom(fiber) { const dom = === 'TEXT_ELEMENT' ? ("") : () updateDom(dom, {}, ) return dom } let nextUnitOfWork = null let wipRoot = null // Save a reference to root fiberlet currentRoot = null // Save the fiber tree corresponding to the current pagelet deletions = null function render(element, container){ wipRoot = { dom: container, props: { children: [element], // At this time, the element is just a virtual dom tree created by the function. }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot } function commitRoot(){ (commitWork) commitWork() currentRoot = wipRoot wipRoot = null } const isEvent = key => ("on") const isProperty = key => key !== "children" && !isEvent(key) const isNew = (prev, next) => key => prev[key] !== next[key] const isGone = (prev, next) => key => !(key in next) function updateDom(dom, prevProps, nextProps) { //Remove old or changed event listeners (prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) ( eventType, prevProps[name] ) }) // Remove old properties (prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // Set new or changed properties (nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // Add event listeners (nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) ( eventType, nextProps[name] ) }) } function commitWork(fiber){ if(!fiber){ return } let domParentFiber = while(!){ domParentFiber = } const domParent = ; if ( === "PLACEMENT" && != null) { () } else if ( === "UPDATE" && != null) { updateDom(, , ) } else if ( === "DELETION") { // () commitDeletion(fiber, domParent) } commitWork() commitWork() } function commitDeletion(fiber, domParent){ if(){ () } else { commitDeletion(, domParent) } } function workLoop(deadline) { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork) shouldYield = () < 1 } if(!nextUnitOfWork && wipRoot){ commitRoot() } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) function reconcileChildren(wipFiber, elements) { let index = 0 let oldFiber = && let prevSibling = null while (index < || oldFiber != null) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && == if (sameType) { newFiber = { type: , props: , dom: , parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } if (element && !sameType) { newFiber = { type: , props: , dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { = "DELETION" (oldFiber) } if (oldFiber) { oldFiber = } if (index === 0) { = newFiber } else if (element) { = newFiber } prevSibling = newFiber index++ } } function performUnitOfWork(fiber) { // 1. The fiber node corresponding to the function component does not have real dom elements // 2. Function components need to run functions to get children const isFunctionComponent = instanceof Function if(!isFunctionComponent && !){ = createDom(fiber) } const children = isFunctionComponent ? [()] : // Step 2 Create a corresponding fiber node for each new react element node and determine whether the real dom element on the old fiber node can be reused. // Save the overhead of creating real dom elements reconcileChildren(fiber, children) // Step 3: Find the next unit of work if(){ return } let nextFiber = fiber while(nextFiber){ if(){ return } nextFiber = } } const MiniReact = { createElement: (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } }, render } /** @jsx */ const container = ("root") function App(props){ return <h1>Hi { }</h1> } const element = <App name="foo" /> (element, container)
Chapter 9 Hooks
The complete code of this chapter
import React from 'react'; function createDom(fiber) { const dom = === 'TEXT_ELEMENT' ? ("") : () updateDom(dom, {}, ) return dom } let nextUnitOfWork = null let wipRoot = null // Save a reference to root fiberlet currentRoot = null // Save the fiber tree corresponding to the current pagelet deletions = null function render(element, container){ wipRoot = { dom: container, props: { children: [element], // At this time, the element is just a virtual dom tree created by the function. }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot } function commitRoot(){ (commitWork) commitWork() currentRoot = wipRoot wipRoot = null } const isEvent = key => ("on") const isProperty = key => key !== "children" && !isEvent(key) const isNew = (prev, next) => key => prev[key] !== next[key] const isGone = (prev, next) => key => !(key in next) function updateDom(dom, prevProps, nextProps) { //Remove old or changed event listeners (prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) ( eventType, prevProps[name] ) }) // Remove old properties (prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // Set new or changed properties (nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // Add event listeners (nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) ( eventType, nextProps[name] ) }) } function commitWork(fiber){ if(!fiber){ return } let domParentFiber = while(!){ domParentFiber = } const domParent = ; if ( === "PLACEMENT" && != null) { () } else if ( === "UPDATE" && != null) { updateDom(, , ) } else if ( === "DELETION") { // () commitDeletion(fiber, domParent) } commitWork() commitWork() } function commitDeletion(fiber, domParent){ if(){ () } else { commitDeletion(, domParent) } } function workLoop(deadline) { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork) shouldYield = () < 1 } if(!nextUnitOfWork && wipRoot){ commitRoot() } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) function reconcileChildren(wipFiber, elements) { let index = 0 let oldFiber = && let prevSibling = null while (index < || oldFiber != null) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && == if (sameType) { newFiber = { type: , props: , dom: , parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } if (element && !sameType) { newFiber = { type: , props: , dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { = "DELETION" (oldFiber) } if (oldFiber) { oldFiber = } if (index === 0) { = newFiber } else if (element) { = newFiber } prevSibling = newFiber index++ } } function performUnitOfWork(fiber) { // 1. The fiber node corresponding to the function component does not have real dom elements // 2. Function components need to run functions to get children const isFunctionComponent = instanceof Function if(!isFunctionComponent && !){ = createDom(fiber) } const children = isFunctionComponent ? updateFunctionComponent(fiber) : // Step 2 Create a corresponding fiber node for each new react element node and determine whether the real dom element on the old fiber node can be reused. // Save the overhead of creating real dom elements reconcileChildren(fiber, children) // Step 3: Find the next unit of work if(){ return } let nextFiber = fiber while(nextFiber){ if(){ return } nextFiber = } } let wipFiber = null let hookIndex = null function updateFunctionComponent(fiber){ wipFiber = fiber hookIndex = 0 = [] return [()] } function useState(initial){ const oldHook = && && [hookIndex] const hook = { state: oldHook ? : initial, queue: [], } const actions = oldHook ? : [] (action => { = action() }) const setState = action => { (action) wipRoot = { dom: , props: , alternate: currentRoot, } nextUnitOfWork = wipRoot deletions = [] } (hook) hookIndex++ return [, setState] } const MiniReact = { createElement: (type, props, ...children) => { return { type, props: { ...props, children: (child => { if(typeof child === 'object'){ return child } return { type: 'TEXT_ELEMENT', props: { nodeValue: child, children: [], } } }) } } }, render, useState, } /** @jsx */ const container = ("root") function Counter(){ const [state, setState] = (1) return ( <h1 onClick={() => setState(c => c + 1)}> Count: { state } </h1> ) } const element = <Counter /> (element, container)
This is the article about the comprehensive and in-depth analysis of the core principles of the React framework. For more related core content of the React framework, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!