Article directory
- Preface
- Background of the emergence of Fiber: problems caused by responsive update features
- Benefits brought by Fiber: splitting tasks and executing them on demand
- Fiber simple principle description
- What does a Fiber object look like?
- What key api is used to realize the principle
- The difference between Fiber and Vue’s responsive update
- Linked list structure of hooks
Foreword
Watching videos on Bilibili and some blogs on the Internet, when talking about fiber, the source code is always explained, and all kinds of high-level conceptual vocabulary are extremely unfriendly to people who are coming into contact with fiber for the first time and want to understand it.
So I read this and that on the Internet, combined my own understanding, sorted it out, and wrote this article explaining fiber in vernacular, hoping to help newcomers.
There are still many loose parts in the article, but these are all for ease of understanding. I believe that when learning something, you must first understand the general outline and then add details.
Background of the emergence of Fiber: problems caused by responsive update features
Friends who have used react should know that when we modify a certain node data of a component, the component will re-update each node, including all sub-components, which is what everyone often calls, top-down Next re-render the component.
If you don’t believe it, you can use node rendering
{Math.random()}
to verify.
During each re-rendering process, a new virtual DOM tree will be regenerated.
If the virtual tree is very complex (for example, there are many complex sub-components nested), the main thread will occupy the time for a long time when doing diff, and the time left for rearrangement and redrawing cannot be guaranteed to be executed 60 times in 1 second, and the page will be It will get stuck (if you don’t understand here, you can read [Intersection of Computer Principles] to discuss and sort out how browsers parse HTML files).
This is a pain point of react15. In order to solve this pain point, version 16 launched fiber.
Benefits brought by Fiber: splitting tasks and executing on demand
To put it simply, using fiber can cut the diff task into very small tasks. Every time you want to do these small tasks, check whether the main thread has an idle period every 16.6ms, and if so, insert them into the execution.
And he also has the ability. To give a loose analogy, for example, a certain diff task needs to be divided into 100 small tasks. He does not divide them all at once, but inserts the main tasks every 16.6ms while dividing. Executed while the thread is idle.
Simple principle description of Fiber
The essence of fiber is actually a js object, which is a packaging object in the intermediate process from the virtual dom node object to the view.
When traversing the virtual tree in the diff algorithm, depth first is used. In react15, the traversal must be completed all at once, that is, the traversal task is only once.
Then we can transform each virtual dom node, and record the node objects pointing to parent, child, and sibling nodes.
What are the benefits of this? When the traversal task is interrupted, we can record the location of the traversed node. When we continue to traverse next time, we can find the node where the traversal task is suspended through the recorded position, and continue traversing through the pointing relationship.
OK, then can we use this idea to segment fine-grained tasks?
The process of adding real DOM attributes to each virtual DOM node and adding various relationship pointing is a simple small task, also called Unit Task. I call it fiberization.
For example, if a DOM tree has 100 nodes, it can be divided into 100 unit tasks and needs to be fiberized 100 times.
This process can be described as follows:
When the main thread is idle, it traverses the virtual tree and fiberizes each node. At this time, if 20 nodes are fiberized, 20 unit tasks are completed. At this time, the main thread is occupied by other tasks and records the traversed node positions. Then the main thread becomes idle again and continues to traverse from the 20th node until all the child nodes found after returning to the root node have been fiberized, and all tasks are completed.
What does a Fiber object look like
I’ll give you an excerpt from the Internet. In fact, you just need to know the direction and the type:
type Fiber = {<!-- --> // The WorkTag type used to mark the fiber, mainly represents the component type represented by the current fiber, such as FunctionComponent, ClassComponent, etc. tag: WorkTag, // key in ReactElement key: null | string, // ReactElement.type, the first parameter of calling `createElement` elementType: any, // The resolved function/class/ associated with this fiber. // Indicates the node type currently represented type: any, //Represents the element component instance corresponding to the current FiberNode stateNode: any, // Point to his `parent` in the Fiber node tree, used to return upward after processing this node return: Fiber | null, // point to its first child node child: Fiber | null, // Points to its own sibling structure, and the return of the sibling node points to the same parent node sibling: Fiber | null, index: number, ref: null | (((handle: mixed) => void) & amp; {<!-- --> _stringRef: ?string }) | RefObject, // The component props object in the current processing process pendingProps: any, // props after the last rendering memorizedProps: any, // Updates generated by the component corresponding to the Fiber will be stored in this queue. updateQueue: UpdateQueue<any> | null, //The state of the last rendering memorizedState: any, // A list to store the context that this Fiber depends on firstContextDependency: ContextDependency<mixed> | null, mode: TypeOfMode, //Effect // Used to record Side Effect effectTag: SideEffectTag, //Singly linked list is used to quickly find the next side effect nextEffect: Fiber | null, //The first side effect in the subtree firstEffect: Fiber | null, //The last side effect in the subtree lastEffect: Fiber | null, // Represents the time point at which the task should be completed in the future. Later versions will be renamed lanes expirationTime: ExpirationTime, // Quickly determine if there are changes in the subtree that are not waiting for childExpirationTime: ExpirationTime, //fiber version pool, which records the fiber update process for easy recovery alternate: Fiber | null, }
What key APIs should be used to implement the principle?
In fact, strictly speaking, we are asking about the implementation principle of Scheduler, because it is he who does the task segmentation and dispatch.
I heard on the Internet that it was implemented using the pseudo requestIdleCallback that was used before. Because the compatibility of requestIdleCallback is very poor, for example, Safari does not support it directly.
Then react18 used MessageChannel
If you want to know more about it, you can refer to Why React Scheduler is implemented using MessageChannel
The difference between responsive updates between Fiber and Vue
Anyone who has used Vue knows that Vue’s responsive update is a precise update. For example, if a node of a component changes, just update this node. The proxy is used (vue2 is defineProperty), but each responsive variable requires a proxy, and each component collects a lot of dependencies, so the performance is not perfect.
I can only say that both have their own merits.
Linked list structure of hooks
We can find such an attribute memorizedState on the fiber object. This attribute records the calling sequence of hooks inside the component, for example:
const [ str, setStr ] = useState('a') useEffect(() => {<!-- --> // return () => {<!-- --> // } }) useLayoutEffect(() => {<!-- --> // return () => {<!-- --> // } })
The relationship in memorizedState is probably like this: useState--->useEffect--->useLayoutEffect
Because it is necessary to record the linked list relationship of this hook, hooks can only be used in the highest-level scope. (For example, if there is a conditional statement that also contains a hook, where in the linked list is it inserted? So it is impossible to let this happen)
If you have the energy, you can read this article for a more in-depth understanding of the linked list of hooks in react: sorting out the principles and differences between useEffect and useLayoutEffect
I haven’t figured it out yet, so I’ll come back to it later when I’m free