
2025-05-02 日报 Day174

今日的鸡汤
真正的勤勉,从来不是盲目地忙,而是时时有所创造、事事有所成就、处处有所精进。
今日学习内容
1、https://pomb.us/build-your-own-react/
今日笔记
1、Step Three: Concurrent Mode
But… before we start adding more code we need a refactor.
There’s a problem with this recursive call.
Once we start rendering, we won’t stop until we have rendered the complete element tree. If the element tree is big, it may block the main thread for too long.
And if the browser needs to do high priority stuff like handling user input or keeping an animation smooth, it will have to wait until the render finishes.
So we are going to break the work into small units, and after we finish each unit we’ll let the browser interrupt the rendering if there’s anything else that needs to be done.
1 | let nextUnitOfWork = null |
React doesn’t use requestIdleCallback anymore. Now it uses the scheduler package. But for this use case it’s conceptually the same.
2、Step Four: Fibers
To organize the units of work we’ll need a data structure: a fiber tree.
We’ll have one fiber for each element and each fiber will be a unit of work.
Let me show you with an example.
Suppose we want to render an element tree like this one:
1 | React.render( |
In the render we’ll create the root fiber and set it as the nextUnitOfWork. The rest of the work will happen on the performUnitOfWork function, there we will do three things for each fiber:
1、add the element to the DOM
2、create the fibers for the element’s children
3、select the next unit of work
One of the goals of this data structure is to make it easy to find the next unit of work. That’s why each fiber has a link to its first child, its next sibling and its parent.
When we finish performing work on a fiber, if it has a child that fiber will be the next unit of work.
From our example, when we finish working on the div fiber the next unit of work will be the h1 fiber.
If the fiber doesn’t have a child, we use the sibling as the next unit of work.
For example, the p fiber doesn’t have a child so we move to the a fiber after finishing it.
And if the fiber doesn’t have a child nor a sibling we go to the “uncle”: the sibling of the parent. Like a and h2 fibers from the example.
Also, if the parent doesn’t have a sibling, we keep going up through the parents until we find one with a sibling or until we reach the root. If we have reached the root, it means we have finished performing all the work for this render.
1 | function render(element, container){ |
1 |
|
3、Step Five: Render and Commit Phases
We are adding a new node to the DOM each time we work on an element. And, remember, the browser could interrupt our work before we finish rendering the whole tree. In that case, the user will see an incomplete UI. And we don’t want that.
Instead, we’ll keep track of the root of the fiber tree. We call it the work in progress root or wipRoot.
1 | function commitRoot() { |
And once we finish all the work (we know it because there isn’t a next unit of work) we commit the whole fiber tree to the DOM.
4、Step Six: Reconciliation
So far we only added stuff to the DOM, but what about updating or deleting nodes?
That’s what we are going to do now, we need to compare the elements we receive on the render function to the last fiber tree we committed to the DOM.
So we need to save a reference to that “last fiber tree we committed to the DOM” after we finish the commit. We call it currentRoot.
We also add the alternate property to every fiber. This property is a link to the old fiber, the fiber that we committed to the DOM in the previous commit phase.
function performUnitOfWork(fiber){
if(!fiber.dom){
fiber.dom = createDom(fiber)
}
const element = fiber.props.children
let index = 0
let preSibling = null
while(index < elements.length){
const element = elements[index]
const newFiber = {
type: element.type,
props: element.props,
parent: fiber,
dom: null
}
if(index === 0){
fiber.child = newFiber // first child
} else {
preSibling.sibling = newFiber // next sibling
}
preSibling = newFiber // update previous sibling
index++
}
if(fiber.child){
return fiber.child
}
let nextFiber = fiber
while(nextFiber){
if(nextFiber.sibling){
return nextFiber.sibling
}
nextFiber = nextFiber.parent
}
}
function reconcileChildren(wipFiber, elements){
let index = 0
let oldFiber = wipFiber.alternate && wipFiber.alternate.child
let preSibling = null
while(
index < elements.length ||
oldFiber !== null
) {
const element = elements[index]
let newFiber = null
// Compare oldFiber to element
const sameType = oldFiber && element && oldFiber.type === element.type
if(sameType){
// Update the old fiber
newFiber = {
type: oldFiber.type,
props: element.props,
dom: oldFiber.dom,
parent: wipFiber,
alternate: oldFiber, // link to the old fiber
}
}
if(element && !sameType){
// Add a new fiber
newFiber = {
type: element.type,
props: element.props,
dom: null,
parent: wipFiber,
}
}
if(oldFiber && !sameType){
// Delete the old fiber
oldFiber.effectTag = "DELETION"
}
if(oldFiber) {
oldFiber = oldFiber.sibling // move to the next sibling
}
if(index === 0){
wipFiber.child = newFiber // first child
} else {
preSibling.sibling = newFiber // next sibling
}
preSibling = newFiber // update previous sibling
index++
}
}