Why the React Fiber architecture

React 15 Stack ReconcilerIs to update child components recursively. The update, once started, cannot be interrupted because it is executed recursively. When the hierarchy is deep and the recursive update time exceeds 16ms, the user interaction freezes. React16 Fiber ReconcilerBy breaking down the diff algorithm into smaller pieces. When a small piece of execution is complete, the browser determines whether there is time to continue executing a new task, terminates the execution if there is no time, checks whether there is a new and higher priority task in the task list if there is time, does the new task, and keeps repeating the operation.

What is the React Fiber architecture

The simple idea is to break down a long task into units of work (each unit of work is short, but the total time is still long). Before executing the unit of work, the browser determines whether there is free time to execute the unit of work, executes it when there is, and continues to determine whether there is free time when the unit of work is executed. Terminate execution when there is no time to let the browser perform other tasks (such as GUI threads, etc.). Wait until the next frame is executed to determine if there is any free time. If there is, continue the unit of work from where you left off and repeat until the task is finished.

Fiber architecture = Fiber node + Fiber scheduling algorithm

In order for a terminated task to resume execution, the next unit of work must be known. So to connect units of work, use linked lists, save the pointer to the next unit of work in each unit of work, and then resume the execution of the task.

RequestIdleCallback To know the free time for each frame, you need to use the requestIdleCallback Api. The callback function is passed in, which takes an argument (time left), executes the unit of work if there is time left, and continues with requestIdleCallback if there is not enough time, waiting for the next frame to continue.

React Fiber architecture in React

The data structure uses Fiber nodes instead of the virtual DOM’s original structure.

// List structure
export type Fiber = {
  // Fiber type information
  type: any,
  // Local state related to the current Fiber (e.g. browser environment is DOM node)
  stateNode: any,
  ...
  // Point to the parent node, or render the node's components
  return: Fiber | null.// points to the first child node
  child: Fiber | null.// points to the next sibling node
  sibling: Fiber | null,}Copy the code

Introduction to coordination stagethroughReactDOM.render()setStateTasks to be updated are queued and then requested by the browser for scheduling via requestIdleCallback.

// Update the node into the array
updateQueue.push(updateTask);
requestIdleCallback(performWork, {timeout});
Copy the code

Now the browser is idle or timed out and calls performWork to perform the task:

// performWork will get a Deadline indicating the remaining time
function performWork(deadline) {
  // Loop out the tasks in updateQueue
  while (updateQueue.length > 0 && deadline.timeRemaining() > ENOUGH_TIME) {
    workLoop(deadline);// 
  }
  // If you cannot complete all tasks in this execution, request the browser to schedule again
  if (updateQueue.length > 0) { requestIdleCallback(performWork); }}Copy the code

The nextUnitOfWork in nextUnitOfWork is Fiber structure, so it can be resumed after termination.

// Save the current processing scene
let nextUnitOfWork: Fiber | undefined // Save the next unit of work to be processed
let topWork: Fiber | undefined        // Save the first work unit
function workLoop(deadline: IdleDeadline) {
  UpdateQueue retrieves the next execution unit or restores the last interrupted execution unit
  if (nextUnitOfWork == null) {
    nextUnitOfWork = topWork = getNextUnitOfWork();
  }
  // Check the remaining time after each execution unit
  // If it is interrupted, the next execution will start from nextUnitOfWork
  while (nextUnitOfWork && deadline.timeRemaining() > ENOUGH_TIME) {
    // Process the node and return the next node to be processed
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork, topWork);
  }

  // Submit the work, when all the tasks are completed, all updates are executed synchronously
  if (pendingCommit) {
  	/ / the commit phasecommitAllWork(pendingCommit); }}/** * returns the next nextUnitOfWork * to be processed@params Fiber Current node to process *@params TopWork is the root of this update */
function performUnitOfWork(fiber: Fiber, topWork: Fiber) {
  // Process this node
  // The diff algorithm labels the modified node
  // Generate the corresponding stateNode on fiber (real DOM node)
  beginWork(fiber);
  // If there are child nodes, the child node is the next to be processed
  if (fiber.child) {
    return fiber.child;
  }
  // There are no child nodes
  let temp = fiber;
  while (temp) {
    completeWork(temp);// Collect side effects function commit phase execution
    // To the top node, exit
    if (temp === topWork) {
      break
    }
    // The sibling node is the next to be processed
    if (temp.sibling) {
      return temp.sibling;
    }
    // No, go back uptemp = temp.return; }}Copy the code

The diff algorithmCompare the steps

Introduction to render PhaseThe coordination phase is completedWorkInProgress Tree, there are modificationsFiberEach node has a label that loops through the Renderer stageWorkInProgress TreeModify the node and render it to the page.

// After all tasks are executed, enter commit to modify the real Tree
function commitAllWork(fiber) {
  if(! fiber) {return;
  }

  const parentDom = fiber.return.dom;
  if(fiber.effectTag === 'REPLACEMENT' && fiber.dom) {
    parentDom.appendChild(fiber.dom);
  } else if(fiber.effectTag === 'DELETION') {
    parentDom.removeChild(fiber.dom);
  } else if(fiber.effectTag === 'UPDATE' && fiber.dom) {
    // Update DOM attributes
    updateDom(fiber.dom, fiber.alternate.props, fiber.props);
  }

  // Operate recursively on child and sibling elements
  commitRootImpl(fiber.child);
  commitRootImpl(fiber.sibling);
}
Copy the code

Refer to the article

React Fiber: This is probably the most popular way to open a React Fiber.