build-mini-react

createElement

原码:

const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
)
const container = document.getElementById("root")
ReactDOM.render(element, container)

react创建元素的方法如下:

const element = React.createElement(
  "div",
  { id: "foo" },
  React.createElement("a", null, "bar"),
  React.createElement("b")
)

实际上就是:

React.createElement(
  type,
  [props],
  [...children]
)

我们可以看作是创建了一个包含type和props的对象:

React.createElement(
  type,
  props: {
      ...props,
      children
  }
)

举例:
createElement("div", null, a, b) 相当于

{
  "type": "div",
  "props": { "children": [a, b] }
}

render

render函数最初源码如下:

function render(element, container) {
  const dom = document.createElement(element.type)
​
  container.appendChild(dom)
}

为了渲染子元素,需要递归调用render

function render(element, container) {
  const dom = document.createElement(element.type)
​
  element.props.children.forEach(child =>
    render(child, dom)
  )
  
  container.appendChild(dom)
}

给元素添加属性:

function render(element, container) {
    const dom = .....
    
    const isProperty = key => key !== "children"
    Object.keys(element.props)
        .filter(isProperty)
        .forEach(name => {
          dom[name] = element.props[name]
        })
    
    element.props.children.forEach(child =>
        render(child, dom)
    )
​
    container.appendChild(dom)
}

在上述render的实现函数中,在数据量大的情况下,dom的渲染会严重阻塞主流程,如下代码:

element.props.children.forEach(child =>
        render(child, dom)
    )

所以需要化整为零,将dom的渲染拆分成一个个小的工作单元, 在workloop函数中进行递归。当任何一个小的工作单元完成时,浏览器都可以中断渲染去进行更高优先级的工作。

使用requestIdleCallback去优化代码。在主线程空闲时,浏览器会调用requestIdleCallback的回调函数.

function workloop(deadline) {
  .....

  requestIdleCallback(workloop)
}

requestIdleCallback(workloop)

在workloop中需要创建一个performUnitOfWork去执行每一个小的work unit.

function workloop(deadline) {
  ...
  while(nextUnitOfWork) {
    ...
    performUnitOfWork(nextUnitOfWork)
    ...
  }

  requestIdleCallback(workloop)
}

requestIdleCallback(workloop)

function performUnitOfWork(nextUnitOfWork) {
  // TODO
}

Fibers

fiber tree 是一个数据结构,用于处理好每一个工作单元(work unit)之间的联系。

每一个元素都是一个fiber,每一个fiber对应一个工作单元。

比如,我们渲染如下DOM:

React.render(
  <div>
    <h1>
      <p />
      <a />
    </h1>
    <h2 />
  </div>,
  container
)

image

在render函数中,创建了一个root fiber,并把它设置成nextUnitOfWork。剩下的事情,交给performUnitOfWork函数处理。

在performUnitOfWork中,将为每一个fiber做三件事:

  1. 将元素添加到dom中
  2. 为子元素创建fiber
  3. 选择下一个工作单元

当我们完成了对一个fiber的操作,如果它有子元素,那么这个子元素的fiber将是下一个工作单元。在本例中,如果我们完成的是div元素的fiber操作,那么下一个工作单元将是h1的fiber。

如果这个fiber没有child,将查找sibling fiber作为下一个工作单元。例如p元素没有child,那么a元素的fiber便成了next unit work.

如果这个fiber既没有child,也没有sibling,则将查找他的parent的sibling。比如a元素fiber的下一个工作单元就是h2的fiber。

如果parent也没有sibling,那我们就一直向上查找,一直到root fiber。

参考

posted @ 2022-07-26 15:13  webLion200  阅读(25)  评论(0编辑  收藏  举报