This is the 24th day of my participation in the Gwen Challenge.More article challenges
preface
In the last section, we implemented creating a task queue, adding a task, and using the requestIdelCallbackapi to implement the scheduling logic of the task using the idle time of the browser. Next we build a Fiber object with code.
- Construct the root node Fiber object
- Construct the sub-node Fiber object
- Improved stateNode and Tag properties for Fiber objects
- Construct the remaining child node Fiber object in the left node number
- Construct the Fiber object for the remaining child nodes
Construct the root node Fiber object
In the last chapter, after we finish the task scheduling, we need to execute the task. What is the task like? This task is to build Fiber objects for each VirtualDOM object based on the node.
How should this task be performed? Or what node are we going to start building from? What is the order of node construction? See below:
We start building from the outermost layer, the root node of the VirtualDOM tree, and then the next two child nodes. After building, the relationship between the three components is specified. Only the first child from the left is the parent child. Build the second node from left to right as the next sibling of the first child. Level to determine the relationship, and then go to the first child node of the child nodes, or the most on the left side of the Fiber to build this node object, after the completion of the building, in building the child’s two children, and then to determine the relationship between them, to determine the relationship between later found no children, she will go to find, at the same level of child nodes according to the depth traversal sequence to build.
Let’s try to build the Fiber object of the root node, the node with id root, and its children are the children wrapped in the outermost div in JSX.
<div>
<p>Hello React</p>
</div>
Copy the code
Let’s review the flow of task scheduling. When the Render method is called, we add an object to the task queue with two properties, one dom, and currently the root node is the root node with the id root and one is props. The value is an object, the children property of this object, whose value is the child node of the parent. PerformTask requestIdelCallback(performTask) is executed when the browser is idle. If the subTask is null for the first time, then getFirstTask will be called to retrieve the task.
GetFirstTask is a method that we have done only one function initialization before, so let’s do it now. GetFirstTask gets the first small task in the queue, and from the first small task object, constructs the fiber object of the root node.
// The basic structure of the fiber object{type Node type (element, text, Components) (type) of props and adding attributes stateNode node of the DOM object | component instance object tag tag node (for specific types of classification hostRoot | | hostComponent | | classComponent | | FunctionComponent) An array of Effects that stores the fiber object effectTag that is currently being performed on the fiber (add, delete, Modify) the parent of the current Fiber under a parent child current Fiber of Fiber under a child level Fiber (current Fiber under one brother Fiber alternate backup Fiber Fiber alignment you use}Copy the code
For the root fiber object, we don’t need to specify the type attribute. We build the props attribute (task.props. StateNode) to store the DOM object of the current node, tag is a tag, and the root object is host_root. We’re not going to get this array and we’re going to give an empty array as a value for now, because we don’t need it yet. The root node also does not need to use effectTag attributes because there are no additions, deletions, or modifications. There is also no parent because this is the root node, and finally need to configure a child node property, the value is not currently specified as null, alternate is used in comparison, for now not specified.
const getFirstTask = () = > {
/** * Get the task */ from the task queue
const task = taskQueue.pop()
/** * Returns the outermost node's fiber object */
return {
props: task.props,
stateNode: task.dom,
tag: "host_root".effects: [].child: null}}Copy the code
This object is returned and assigned to the subTask
if(! subTask) { subTask = getFirstTask()console.log(subTask)
}
Copy the code
Effect:
A root fiber object is built!!
Once we have the Fiber object and the browser has some free time, we execute the while loop in the workLoop method, which passes the root Fiber object to the executeTask method, and we have the whole task scheduling and Fiber object construction up and running
const workLoop = deadline= > {
if(! subTask) {// subTask is a fiber object
subTask = getFirstTask()
}
while(subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask)
}
}
Copy the code
You now know that the executeTask parameter is named Fiber. Because what you get is the fiber object for the root node
Construct the sub-node Fiber object
The construction of the FIber object for the child nodes is completed in the executeTask method, which calls the reconcileChildren method, with FIber as the first argument, The second argument is the child VitrualDOM object obtained from fiber.props. Children,
1. Implementation of the reconcileChildren approach
The second argument to this method is children, which could be an object, which could be an array, when we call the Render method children, we pass Element as an object that’s an object, If we don’t pass it and createElement returns it then children is an array
export const render = (element, dom) = > {
taskQueue.push({
dom,
props: {children: element} // This element is an object
})
createElement.js
// children is an array
export default function createElement(type, props, ... children) {
constchildElements = [].concat(... children).reduce((result, child) = > {
if(child ! = =false&& child ! = =true&& child ! = =null) {
if (child instanceof Object) {
result.push(child)
} else {
result.push(createElement("text", { textContent: child }))
}
}
return result
}, [])
return {
type,
props: Object.assign({ children: childElements }, props),
}
}
Copy the code
Ideas:
Because it could be an array or an object, and that’s something that specifically affects what happens later in the code, we define a method that checks whether the argument is an array, returns it if it’s an array, and returns it if it’s an object wrapped in an array.
Implementation: We created an Arrified folder in the miscellaneous Misc directory where we want to create index.js to convert the children object to an array
const arrified = arg= > Array.isArray(arg) ? arg : [arg]
export default arrified
const reconcileChildren = (fiber, children) = > {
/** * children can be objects or arrays * convert children to arrays */
console.log(children)
const arrifiedChildren = arrified(children);
console.log(arrifiedChildren)
}
Copy the code
Next, convert the VitrualDOM in the arrifiedChildren array to a Fiber object. We need a loop to build the VirtualDOM from the array into a Fiber object
const reconcileChildren = (fiber, children) = > {
/** * children can be objects or arrays * convert children to arrays */
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null
while(index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element.type,
props: element.props,
tag: "host_component".effects: [].effectTag: "placement".stateName: null.parent: fiber
}
fiber.child = newFiber
index++
}
}
Copy the code
At this point, we’re done building each DOM object into Fiber, and the Types and props are derived directly from the Element. We haven’t dealt with the component node yet, and only one root node has been generated before. The tag is “host_component”. Effects is defined as an empty array. EffectTag is just the identification of the operation called placement, stateNode is the DOM object of the current node, and we’ll perfect that in a second and let’s call it null for now. Here we are almost done with the initial construction of a child node, but we still need to add the node relationship properties, who is the parent of whom, and who is a subset of whom. It is clear that the current fiber passed in is the parent of the node Fiber object. In this case we also add a child to Fiber with the value newFiber.
There are a few minor problems here. If you have multiple children, the first child is the child of the parent, the other children are all siblings to each other, the second is the first sibling, but the three are second siblings. So it is the first child that sets the child property, and all other sibling nodes need to be set
const reconcileChildren = (fiber, children) = > {
/** * children can be objects or arrays * convert children to arrays */
const arrifiedChildren = arrified(children);
let index = 0;
let numberOfElements = arrifiedChildren.length;
let element = null;
let newFiber = null
let prevFiber = null
while(index < numberOfElements) {
element = arrifiedChildren[index];
newFiber = {
type: element type,...}// 0 is a child node, and the others are siblings of each other
if(index === 0) {
fiber.child = newFiber;
} else {
prevFiber.sibling = newFiber
}
// Finally store the previous fiber object after processing
prevFiber = newFiber;
index++
Copy the code
2. Set the stateNode property
The stateNode attribute is obtained using the createStateNode function call. The required parameter is the current node fiber object newFiber. CreateStateNode generates a DOM element object when the fiber tag is “host_component”. If it is a component, it should store the component instance object. CreateDOMElement to generate the corresponding normal DOM element object.
// react/reconciliation/index.js
newFiber.stateNode = createStateNode(newFiber)
// src/react/Misc/createStateNode/index.js
import { createDOMElement } from '.. /.. /DOM'
const createStateNode = fiber= > {
if(fiber.tag === "host_component") {
return createDOMElement(fiber)
}
}
export default createStateNode
Copy the code
Create a DOM folder in Misc and createdomelement.js for DOM elements
// src/react/DOM/createDOMElement.js
import updateNodeElement from "./updateNodeElement"
export default function createDOMElement(virtualDOM) {
let newElement = null
if (virtualDOM.type === "text") {
// Text node
newElement = document.createTextNode(virtualDOM.props.textContent)
} else {
// Element node
newElement = document.createElement(virtualDOM.type)
updateNodeElement(newElement, virtualDOM)
}
return newElement
}
Copy the code
UpdateNodeElement updateNodeElement updateNodeElement updateNodeElement updateNodeElement updateNodeElement updateNodeElement updateNodeElement updateNodeElement updateNodeElement updateNodeElement
You can see that the stateNode has been generated.
3. Set the tag attribute
When we deal with child nodes, each child node has a different type. We have a method to determine the type of node and set the value of the tag by the different type.
// src/react/Misc/getTag/index.js
const getTag = vdom= > {
if(typeof vdom.type === 'string') {
return "host_component"}}export default getTag
Copy the code
4. Construct the remaining child node Fiber object in the left node number
Now that the root node has been associated with its first layer child node and its sibling nodes, we need to go down to the child node and continue building the Fiber object.
In the same order as before, we should find the left-most child node of the second layer and start building the Fiber object. How should we build this node? In the code, we use the rule of least rule to find children to find a node (we can use the root node as an example) and see if it has children. If it does, a Fiber object is created by passing this sublevel and the children object from this sublevel to the reconcileChildren(Fiber, fiber.props. Children). The reconcileChildren continues to be created by passing the child’s children to the reconcileChildren as the father.
const executeTask = fiber= > {
reconcileChildren(fiber, fiber.props.children)
// Check whether the current fiber.child has a value, if so, return fiber.child
if(fiber.child) {
return fiber.child
}
console.log(fiber)
}
const workLoop = deadline= >{...while(subTask && deadline.timeRemaining() > 1) {
subTask = executeTask(subTask)
}
}
Copy the code
This code execution is pretty confusing, even though it just adds a judgment that returns fiber.child. When the reconcileChildren, Fiber. child is returned with a value, executeTask returns a new Fiber object, and the subTask is reassigned to the reconcileChildren, Fiber. child in the workLoop method and execution continues. The loop ends when the executeTask child node is empty. Thus the left child node tree DOM builds the Fiber object.
5. Construct fiber objects for the remaining child nodes
Now that all the children on the left have been built, we need to build fiber objects for all the remaining nodes. When the left node is built, we should locate the last child node, and find the remaining nodes according to the last child node. If the current node has a sibling, we will build the node. If not, go back to his parent, check if his parent has any siblings, keep going back and look for build Fiber objects, and build fiber objects for all remaining children
// Add one more p node
import React, {render} from "./react"
const root = document.getElementById("root")
const jsx = (<div>
<p>Hello React</p>
<p>I'm the sibling child</p>
</div>)
console.log(jsx)
render(jsx, root)
let currentExecutedFiber = fiber
while(currentExecutedFiber.parent) {
// there are sibling returns to sibling
if(currentExecutedFiber.sibling){
return currentExecutedFiber.sibling
}
// Back to the parent level
currentExecutedFiber = currentExecutedFiber.parent
}
Copy the code
This completes the fiber object construction for all the child nodes!!
conclusion
Today, WE have completed the construction of fiber objects for the root node and all its children. After obtaining the vittualDOM of the root node, we will create the root Fiber object according to the previously provided Fiber object model. The first layer passes the first parameter fiber and the second parameter fiber. Props. Children creates the current child object. The subsequent child node continues the logic to generate the fiber object. If the child attribute has a value, friber.child. After creating the left child node, the code runs to the last child node. If the child node has a sibling, it generates fiber until the sibling is empty. Go back to the upper level and complete the creation of all fiber objects
All right!! In the next section, we will learn how to build an Array of Effects and enter fiber phase 2 commit to implement initial rendering.