Don’t row away. Trust me, you’re gonna get something!

The article is based on React 16.8

We’ll rewrite React step by step from scratch. Follow the architecture in real React code, but without all the optimizations and non-essential features.

directory

  1. createElement
  2. render
  3. Concurrent Mode
  4. Fibers
  5. Render and Commit phases
  6. Reconciliation
  7. Function Components
  8. Hooks

Fibers

From the last article, we learned that we need to store the next unit to be processed in nextUnitOfWork and continue processing when the browser is idle. I think you must have a question in your mind: how do you know the next unit to be processed?

Let’s look at its data structure

const newFiber = {
    type: element.type,  // store string-> table signature | function
    props: element.props, // Store attributes
    parent: fiber, // refer to the parent node source as return property
    sibling: fiber, // point to sibling nodes
    child: fiber, // point to the child node
    dom: null.// Store dom elements
}
Copy the code

Fiber is a Concurrent Mode module with the following functions: 1. Store intermediate state; 【parent/sibling/child】

Next, use examples to help you understand fiber

SunsmileReact.render(
  <div>
    <h1>
      <p />
      <a />
    </h1>
    <h2 />
  </div>,
  container
)
Copy the code

JSX
Fiber tree
Concurrent Mode
Asynchronous interruptibility
child
sibling

Image above:

implementation

  1. inrenderIn the function, we’re going tonextUnitOfWorkSet up theFiberThe root node[Container node]
function render(element, container) {
    nextUnitOfWork = {
        dom: container,
        props: {
            children: [element],
        },
    }
}
Copy the code
  1. inperformUnitOfWorkTo achieve the following functions:
  • createdomNode and add todomIn the tree
  • forchildrenCreate a newFiber
  • Choose the next oneFibernode
// Create a DOM node
function createDom(fiber) {
    const dom =
            fiber.type == "TEXT_ELEMENT"
                    ? document.createTextNode("")
                    : document.createElement(fiber.type)

    const isProperty = key= >key ! = ="children"
    Object.keys(fiber.props)
            .filter(isProperty)
            .forEach(name= > {
                    dom[name] = fiber.props[name]
            })

    return dom
}
function performUnitOfWork(fiber) {
        // Create a DOM node
        if(! fiber.dom) { fiber.dom = createDom(fiber) }// Add to the parent node
        if (fiber.parent) {
                fiber.parent.dom.appendChild(fiber.dom)
        }

        // Create a new Fiber node for the child and add it to the Fiber tree
        const elements = fiber.props.children
        let index = 0
        let prevSibling = null

        while (index < elements.length) {
                const element = elements[index]
                 // Create the fiber node
                const newFiber = {
                        type: element.type,
                        props: element.props,
                        parent: fiber,
                        dom: null,}// Add to the fiber tree
                if (index === 0) {
                        fiber.child = newFiber
                } else {
                        prevSibling.sibling = newFiber
                }

                prevSibling = newFiber
                index++
        }

        // Select the next node
        if (fiber.child) {
                return fiber.child
        }
        let nextFiber = fiber
        while (nextFiber) {
                if (nextFiber.sibling) {
                        return nextFiber.sibling
                }
                nextFiber = nextFiber.parent
        }
}
Copy the code

Stage code: github.com/sunsmile-ls…

Render and Commit phases

We know from Concurrent Mode that the browser interrupts the execution of our code, and based on the above code you must see that there is a problem that the browser may interrupt our work before completing the rendering of the entire tree. In this case, the user will see an incomplete UI.

No suspense, go straight to the solution. React separates building the fiber tree from rendering — the Render and Commit phases.

In the Render phase, only the Fiber tree is created, and in the Commit phase, the fiber tree is rendered onto the page. I’m sure you’re thinking, how? I have to go through it twice.

  1. We need to removeperformUnitOfWorkThe following code in the function:
if (fiber.parent) {
    fiber.parent.dom.appendChild(fiber.dom)
}
Copy the code
  1. Because we’re going through it twice, we need to remember the rootsfibernode
let wipRoot = null
function render(element, container) {
   wipRoot = {
       dom: container,
       props: {
       children: [element],
       },
   }
   nextUnitOfWork = wipRoot
}
Copy the code
  1. After the fiber tree is created, we executeThe commit phase
function workLoop(deadline) {...// Indicates that there is no next processing unit and there is a root node
    if(! nextUnitOfWork && wipRoot) { commitRoot() } requestIdleCallback(workLoop) }Copy the code
  1. Finally we reveal the followingcommitThe real face
function commitRoot() {
      commitWork(wipRoot.child)
      wipRoot = null
}

function commitWork(fiber) {
      if(! fiber) {return
      }
      // Add dom to the page
      const domParent = fiber.parent.dom
      domParent.appendChild(fiber.dom)
      commitWork(fiber.child)
      commitWork(fiber.sibling)
}
Copy the code

At this point, our Render and Commit phases are complete.

See the source at github.com/sunsmile-ls…

To be continued…… All of them, thumbs up, thumbs up for your efforts, haha! In case you get lost, follow the author.