This is the 20th day of my participation in the Genwen Challenge
Hello everyone, I’m Komura. Last time we finished implementing TinyReact, we are going to learn about the so-called Fiber algorithm used in React16, and we will spend several chapters trying to figure out what the Fiber algorithm is!!
Project code, we can continue to follow up learning, mutual encouragement!!
Development configuration environment
Before we learn Fiber, we need to set up a development environment first.
1. Develop the project folder structure
Files/folders | describe |
---|---|
src | Store the original file (all of our Fiber code will be unloaded here) |
dist | Store the client code package file (SRC compiled code is stored here) |
build | Store server-side code package files |
server.js | Store server-side code |
webpack.config.server.js | Server webPack configuration file |
webpack.config.client.js | Client WebPack configuration file |
babel.config.json | Babel configuration file |
package.json | Project documents |
Package. json is entered from the command line:
npm init -y
Copy the code
generate
2. Third-party dependencies of the installation project
dependency | describe |
---|---|
webpack | Module packaging tool |
webpack-cli | Pack command (this is required to execute the webpack command on the command line.) |
webpack-node-externals | Remove modules from the node_modules folder when packaging server-side modules |
@babel/preset-env | Babel presets convert advanced JavaScript syntax |
@babel/preset-react | Babel presets, convert JSX syntax |
babel-loader | The Babel tool loader in Webpack |
nodemon | Monitor server file changes and restart applications |
npm-run-all | Command line tool that can execute multiple commands at the same time |
express | Web development framework based on Node platform |
Installation command:
// Development depends on NPM I webpack webpack-cli webpack-node- externals@babel /preset-env @babel/preset-react babel-loader nodemon Npm-run-all-d // The project relies on NPM I ExpressCopy the code
3. Start a server
We use Express to start a server listening on port 3000
import express from "express"
const app = express()
app.use(express.static("dist"))
const template = ` React Fiber
`
app.get("*".(req, res) = > {
res.send(template)
})
app.listen(3000.() = > console.log("server is running"))
Copy the code
The server code still doesn’t work, and needs to be transformed by Babel alignment to execute the webPack wrapped code. So next we should need to configure Babel and Webpack
4. Configure Babel and Webpack
- Configure Babel to introduce “@babel/preset-env”, “@babel/preset-react” for our ES6 + code to be browser-compatible or Express-compatible
//babel.config.json
{
"presets": ["@babel/preset-env"."@babel/preset-react"]}Copy the code
Default es6+ code and React code conversion configuration
- Set up the server and client WebPack configuration
Ideas:
Path.resolve (__dirname, “build”) is used to specify path.resolve(__dirname, “build”). Then specify the name of the package as ‘server.js’. Also need to configure the packaging rules, we package js, then set the JS packaging configuration, using the tool babel-loader. Finally, configure externals. This configuration tells us not to package modules under node_modules.
Code:
const path = require('path')
const nodeExternals = require('webpack-node-externals')
module.exports = {
target: "node".mode: "development".entry: "./server.js".output: {
path: path.resolve(__dirname, "build"),
filename: "server.js"
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"}}},externals: [nodeExternals()]
}
Copy the code
Ideas:
The configuration on the browser side is similar to that on the server side. We first copy it and modify it. The target is changed to Web, the input file is SRC /index.js, and the output location is dist folder
Code:
const path = require('path')
module.exports = {
target: "web".mode: "development".entry: "./src/index.js".output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
devtool: "source-map".module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"}}}}]Copy the code
We complete the SRC index.js file while configuring the client. With the basic configuration set up, we finally configure the relevant shell commands to start the service in package.json
"main": "babel.config.json", "scripts": { "start": "NPM -run-all --parallel dev:*", // use NPM -run-all to batch run dev:server-compile": "Webpack --config webpack.config.server.js --watch",// run the webapck command "dev:server": "Nodemon./build/server.js", // If the server file changes, restart the server command "dev:client-compile": "Webpack --config webpack.config.client.js --watch"Copy the code
5. Verify the development environment configuration
Add the test code in SRC /index.js to check whether the configuration is successful by introducing the bundled client code file bundle.js into the server template code.
// server.js
const template = ` < HTML >, < div id = "root" > < / div > < script SRC = "bundle. Js" > < / script > / / into bundles. Js... < / HTML > `
//src/index.js
console.log('form client js')
Copy the code
Validation:
Successful output of client code. The configuration is successful !!!! Ha ha ha ha
Know requestIdleCallback
1. Fiber core API function introduction
Use the free time of the browser to execute tasks. If there are higher priority tasks to execute, the current task can be terminated in favor of higher priority tasks
Usage scenario: Now we have a computing task to execute, which takes a long time. During the execution of the task, the main thread of the browser will be occupied all the time. When the main thread is occupied, the browser will be stuck and cannot perform other tasks. If the user wants to scroll down to see the rest of the page at this point, the browser will not respond to the user’s current actions. The feeling to the user is that the page is stuck, which will cause a very poor experience. To solve this problem, we can put the calculation task into the requestIdleCallback callback and execute it in the browser’s idle time. When the user manipulates the page, the higher priority task is executed, the calculation task is terminated, and the user’s action is responded by the browser. The user will not feel the page is stuck and will continue to perform the computing tasks in the requestIdleCallback when the high-priority tasks are completed. It solves the problem that the calculation task takes up the main thread for a long time.
The API function uses requestIdleCallback to receive an argument as the callback function. The callback function receives an argument as the deadline of the browser. We can determine which calculation task to perform according to the deadline.
requestIdleCallback(function(dealine) {
// deadline.timeremaining () Gets the idle time of the browser
})
Copy the code
2. Free browser time
We’ve mentioned browser free time repeatedly, but what exactly is browser free time?
When the page is drawn frame by frame, when the number of frames drawn per second reaches 60, the page is smooth, less than this value, the user will feel stuck; 1s 60 frames, the time allocated to each frame is 1000/60≈16ms, if the execution time of each frame is less than 16ms, it means that the browser has free time; If the task is not completed in the remaining time, the task is stopped and the main task continues to take precedence, meaning that the requestIdleCallback always uses the browser’s free time to execute the task
3. API function experience
Light is always not that vivid, let’s type in the code directly using requestIdleCallback, to get a feel for the real effect
- Ideas:
There are two buttons and a DIV on the page. Click the first button to perform an expensive calculation and keep it in the main thread for a long time. Click the second button to change the background color of the DIV on the page while the calculation is being performed. We know that if the main thread is occupied for a long period of time, the browser will not respond to user actions, that is, the div background color cannot be changed.
RequestIdleCallback is the perfect solution to this problem.
// html
<div id="box"></div>
<button id="btn1">Perform computing tasks</button>
<button id="btn2">Change the background color</button>
// style
<style>
#box{
padding: 20px;
background: palegoldenrod;
}
</style>
// js
<script>
var box = document.getElementById("box")
var btn1 = document.getElementById("btn1")
var btn2 = document.getElementById("btn2")
var number = 99999
var value = 0
function calc() {
while (number > 0) {
value = Math.random() < 0.5 ? Math.random() : Math.random();
console.log(value)
number--
}
}
btn1.onclick = function () {
calc()
}
btn2.onclick = function () {
box.style.background = "green"
}
</script>
Copy the code
Effect:
Use the above code for a long time after stalling, then respond to the discoloration. It’s kind of jammed
Optimization:
When the first button is clicked we put the expensive computation into the callback of the requestIdleCallback function. Note: If the high-level task is executed and the loop is terminated, we should call requestIdleCallback(calc) again to execute the loop
function calc(deadline) {
// Execute this loop when the idle time is greater than 1ms
while (number > 0 && deadline.timeRemaining() > 1) {
value = Math.random() < 0.5 ? Math.random() : Math.random();
console.log(value)
number--
}
// The calculation task should be performed again here
requestIdleCallback(calc)
}
btn1.onclick = function () {
requestIdleCallback(calc)
}
Copy the code
Effect: Click the second button to instantly change the color of the box
Problem with the old Stack algorithm
The old DOM alignment algorithm was called Stack. The process of updating VirtualDOM prior to React16 is implemented in a loop plus recursion. One problem with this method is that once the task starts, it cannot be interrupted. If the application has a large number of components and the main thread is occupied for a long time, The main thread can’t be released until the entire VirtualDOM tree alignment is updated. This leads to some user interactions, animations and other tasks that can’t be performed immediately, and the page freezes, which can affect the user experience.
Core problem: Recursion cannot be interrupted, and the execution of heavy tasks takes a long time. JavaScript is single-threaded, and other tasks cannot be executed at the same time, resulting in task delay and page lag, resulting in poor user experience.
Fiber algorithm
React16 rewrites the React code a lot. Fiber is one of the most important parts of the Code. In fact, Fiber is a new DOM alignment algorithm, Fiber is the name of this algorithm.
1. Fiber solution
- Use the browser’s idle time to perform tasks, and refuse to occupy the main thread for long periods
- Using requestIdleCallback to take advantage of the browser’s idle time, virtualDOM alignment does not occupy the main thread. If there is a higher priority task to be executed, virtualDOM alignment is temporarily suspended and the higher priority task is executed first. Start performing VirtualDOM alignment again so that pages don’t get stuck.
- Discard recursion and just use loops, because loops can be broken
- Since recursion requires layer upon layer entry and layer upon layer exit, this process cannot be interrupted. If VirtualDOM alignment tasks can be terminated, you must abandon recursion and use a loop to complete the Process of VirtualDOM alignment, because the loop can be terminated. As long as the conditions at the end of the loop are preserved, the next time a task is started again, the loop can continue to execute at the end of the previous loop.
- Break tasks down. Break them down into smaller tasks
- In this case, even if the task is terminated before it is completed, the cost of re-executing the task will be much smaller. Therefore, we need to split the task into small tasks. How do you break it up? We used to treat the entire VirtualDOM alignment as a task, but now we treat each node alignment as a task, so that a large task is broken down into smaller tasks.
Why is the new React VirtualDOM alignment algorithm called Fiber? Fiber translates as “Fiber”, which means that the granularity of limiting task execution is very fine, like Fiber.
2. Implementation idea
In the Fiber scenario, the DOM alignment algorithm is split into two parts in order to terminate and continue the task: the first part is the VirtualDOM alignment (also known as building Fiber), and the second part is the update of the real DOM (also known as committing). The VirtualDOM comparison process can be terminated, but the updating of the real DOM can not be terminated.
- Build the Fiber
The React. CreateElement method is called and returns to virtualDOM. The first stage is then performed. The first step is to build the Fiber object. We loop through this VirtualDOM object to find the inner VirtualDOM object. For each of the inner VirtualDOM objects, we build the Fiber object, which is also a JavaScript object. Derived from the VitualDOM object, Fiber objects store more information about nodes in addition to the Type, props, and children properties. One important piece of information is to record what the current node is doing, such as deleting the node, updating the node, or adding the node. Once all fiber objects are built, they are stored in an array. The next step is the second step
DOM initial render: virtualDOM->Fiber->Fiber[]->DOM
- Submit the Commit
In the loop FIber operation, compare newFiber with oldFiber, update the node operation type, and apply the operation to the real DOM object based on the type of operation to be implemented by the storage node in the FIber object.
DOM update operation: newFiber vs oldFiber -> Fiber[] -> DOM
Note: In phase 2, the Fiber object for all nodes is stored in an array, and the original DOM node, regardless of whose child, parent, or sibling, is now the NTH element of the array. That is, the relationship between DOM and DOM nodes is smoothed out. However, in the second stage, we need to build the entire DOM tree before rendering to the page. That is, we need to know who is who’s who’s parent and who’s brother so that we can accurately build our DOM tree.
3. The Fiber object
Therefore, according to the above problem, we not only need to store the update operation (effectTag) of the current fiber object, we also need to store the subset (child) of the current node, store the parent (parent) of the current node, store the sibling of the current node, In this way, we can easily know the relationship between them when we recycle the Fiber array, and thus build a complete DOM node tree. With this in mind, we can write a general 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
conclusion
That’s it for today. At the beginning, we will first configure the code development environment we will learn later, the server and browser side configuration of WebPack, start an Express server task in server.js, and use Babel and WebPack to package the browser and server side executable code. On the server side, Webpack combines Babel to package the Node executable code, and on the client side combines WebAPck and Babel to package the front-end reactJSX and JS code into the browser executable code, completing the basic construction of the development environment.
Then we have a basic understanding of the core API of the Fiber algorithm, requestIdleCallback. We can make a piece of JS code execute in the idle time of the code, and use a piece of code to really feel. RequestIdleCallback solves the problem of blocking the main thread while a heavy task is executing, causing the page to stall
We learned the old version of the Stack algorithm, using the recursive VirtualDOM comparison, because recursion is not broken, when we have a lot of components in the page, back to lead to the page stall. This problem can only be solved by abandoning recursive alignment and using the requestIdleCallbackApi. This is what the Fiber algorithm solves:
- Use the browser’s idle time to perform tasks, and refuse to occupy the main thread for long periods
- Discard recursion and just use loops, because loops can be broken
- Break tasks down. Break them down into smaller tasks
This is the solution of Fiber. Through the solution and ideas, the basic model of fiber object is finally designed:
{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
Today, WE hope that you have a basic understanding of the development of the basic configuration of what we are going to do, and need to know the previous problems of Stack algorithm, and the basic principles and ideas of Fiber, the core of the APIrequestIdleCallback motion perception, the next is the realization of Fiber algorithm, please look forward to!!
If you see this, please like, follow, comment (point out your questions), thank you
Source: https://github.com/zelixag/Fiber/tree/main
Reference tutorial: hook tutorial
React
MDN requestIdleCallback