Fiber Architecture of React Source Code

What is Fiber?

Fiber is also called a fiber. It is the same as the process, thread, and coroutine in the execution process of the program in the operating system. Fiber can be understood as a coroutine in the operating system, so generator can also be understood as a coroutine. Why not? What about using generators as coroutines?
insert image description here

  • When using generator and async and await, it is also contagious, and you need to keep declaring * or similar to async in async and await.
  • The implementation update can be interrupted and continued. The implementation update can have different priorities. The high priority update can interrupt the low priority update. Using the generator can achieve the first purpose, but the high priority update cannot be interrupted. Low priority updates.

two definitions

static data structure

For Fiber, it is also used as a static data structure. For a component, information such as the type of the component and the corresponding Dom node are saved. At this time, the Fiber node is what we call virtual Dom.
There can be multiple RootFibers in a page, but one FiberRootNode is required to manage these RootFibers.

dynamic unit of work

Each Fiber node saves the changed state of the component in this update and the work to be performed (needs to be deleted, inserted into the page, updated...).
For Fiber, we can understand it as Dom stored in memory. The coordinator (Reconciler) in React15 is implemented by recursively calling the execution stack. The coordinator (Reconciler) in React16 is implemented based on Fiber nodes.
For React15, the reconcile in the render phase cannot be interrupted. If a large amount of DOM is operated, there will be a freeze, because the browser will give all the time to the js engine thread for execution, and the GUI rendering thread will be blocked at this time. , causing the page to freeze and unable to respond to the corresponding events of the user.
Therefore, after React16, there is a Scheduler to schedule time slices, and each task is given a certain amount of time. If it is not executed within this time, the execution right must be handed over to the browser for drawing and rearranging, so asynchronous Interrupted updates require a certain data structure to store dom information in memory, so such a data structure Fiber is produced, which can also be called virtual Dom.

Fiber data structure

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
  ) {
    
    
    // Instance
    // tag是Fiber对应的组件类型,比如Funtion Component、Class Component、Hooks Component
    // 其中Hooks Component是指Dom节点对应的Fiber节点
    this.tag = tag;
    this.key = key;
    // elementType大部分情况与type相同,除非Function Component使用React.memo来包裹时,他的ElementType与type不同
    // type对于Funtion Component来说是函数本身,对于Class Component是Class,对于Hooks Component是Dom节点的TagName
    this.elementType = null;
    this.type = null;
    // stateNode对于Hooks Components来说指对应的真实Dom节点
    this.stateNode = null;
    
    // Fiber
    // return/child/sibling会链接储存一颗Fiber树
    this.return = null;
    this.child = null;
    this.sibling = null;
    // 对于多个同级的Fiber节点,代表插入Dom的位置索引
    this.index = 0;
    // 就是我们常用的Ref属性
    this.ref = null;
    
    // 下面的属性都是将Fiber作为动态的工作单元使用时的属性 
    this.pendingProps = pendingProps;
    this.memoizedProps = null;
    this.updateQueue = null;
    this.memoizedState = null;
    this.dependencies = null;
    
    this.mode = mode;
    
    // Effects
    this.flags = NoFlags;
    this.subtreeFlags = NoFlags;
    this.deletions = null;
    
    // 调度优先级
    this.lanes = NoLanes;
    this.childLanes = NoLanes;
    // 关系到Fiber架构的工作方式
    this.alternate = null;
    
    if (enableProfilerTimer) {
    
    
      // Note: The following is done to avoid a v8 performance cliff.
      //
      // Initializing the fields below to smis and later updating them with
      // double values will cause Fibers to end up having separate shapes.
      // This behavior/bug has something to do with Object.preventExtension().
      // Fortunately this only impacts DEV builds.
      // Unfortunately it makes React unusably slow for some applications.
      // To work around this, initialize the fields below with doubles.
      //
      // Learn more about this here:
      // https://github.com/facebook/react/issues/14365
      // https://bugs.chromium.org/p/v8/issues/detail?id=8538
      this.actualDuration = Number.NaN;
      this.actualStartTime = Number.NaN;
      this.selfBaseDuration = Number.NaN;
      this.treeBaseDuration = Number.NaN;
    
      // It's okay to replace the initial doubles with smis after initialization.
      // This won't trigger the performance cliff mentioned above,
      // and it simplifies other profiler code (including DevTools).
      this.actualDuration = 0;
      this.actualStartTime = -1;
      this.selfBaseDuration = 0;
      this.treeBaseDuration = 0;
    }
    
    if (__DEV__) {
    
    
      // This isn't directly used but is handy for debugging internals:
    
      this._debugSource = null;
      this._debugOwner = null;
      this._debugNeedsRemount = false;
      this._debugHookTypes = null;
      if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
    
    
        Object.preventExtensions(this);
      }
    }
}

Fiber double buffer

What is double buffering? We can use animation as an example.
An animation consists of multiple consecutive pictures. Each picture can become a frame of the animation. When we play the animation, we need to display each frame continuously at a certain speed, and render a new frame of pictures. It is necessary to clear the picture of the previous frame. If the picture displayed between the clearing of the previous frame and the next frame is too long, there will be a white screen flickering that the human eye can perceive. In order to solve this problem, we can start from the memory Draw the picture of the current frame in the drawing, and directly replace the picture of the previous frame with the current frame after drawing, because the time consumed by replacing these two frames will not appear from the white screen to the flickering of the picture (the speed is fast enough), This technique of building and replacing directly in memory is called double buffering.
Example:
For such a Dom structure, the following Fiber will be generated

function App() {
    
    
  return (
    <div>
      zi
      <p>bai</p>
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById("root"));

The Fiber diagram for the above structure is as follows:
insert image description here

mount

Regarding the previous explanation of the definition of Fiber, we know that Fiber, as a virtual Dom, can store and describe the real DOM. When mounting, the jsx object will be constructed as a Fiber object to form a Fiber tree. This Fiber tree is called current Fiber and corresponds to Real dom on. The Fiber tree being built is called workInProgress Fiber, and the nodes of the two trees will be connected through alternate.
For the first rendering of the page, ReactDom.Render will be used to create FiberRootNode. For fiberRoot, it refers to the root node of the entire application, and there is only one. Each time ReactDom.Render is called, the root node RootFiber of the current application will be created. RootFiber refers to the node created by ReactDom.render or ReactDOM.unstable_createRoot. There can be more than one of which will have a current pointer pointing to the RootFiber node. The page is blank before the screen rendering, so RootFiber has no child nodes. Next, no matter whether it is the first screen rendering or the update created by calling this.setState or calling the Hooks method of useState, a Fiber tree will be created from the root node.
Only two nodes, FiberRootNode and rootFiber, will be created during Mount. The initial time is as follows:
insert image description here
Then create workInProgress Fiber according to jsx, and then link through alternate.
insert image description here
Then workInProgress Fiber will exchange positions with current Fiber. At this time, workInProgress becomes current Fiber and renders into the corresponding real Dom as shown below:
insert image description here

update

For updating, we can understand it as an animation frame, and the replacement of double buffering is analogous to two frames of animation. When the next frame of animation replaces the previous frame of animation fast enough, there will be no stuttering. At this point, let’s compare it to Fiber. When it is updated, workInProgress will be generated in memory, and then workInProgress will be replaced by current Fiber and the current pointer will point to workInProgress Fiber and rendered as the corresponding Dom. If the execution speed is fast enough, there will be no lag The phenomenon. The specific process is as follows:
when updating, a new workInProgress Fiber will be formed based on the comparison between the current Fiber and the jsx object after the state change. The process is also the diff algorithm, and then the workInProgress Fiber is switched to the current Fiber and applied to the real DOM to achieve the purpose of updating , all of this happens in memory, reducing the manipulation of the dom.
insert image description here
Finally, switch workInProgress Fiber to current Fiber.
insert image description here
When the above is update, the changes between current Fiber and workInProgress Fiber

Guess you like

Origin blog.csdn.net/liu19721018/article/details/127841970