美文网首页
React 源码探源 2 Mount 复杂的例子

React 源码探源 2 Mount 复杂的例子

作者: 吴摩西 | 来源:发表于2021-06-21 17:51 被阅读0次

接第一章React 源码探源 1 Mount.

本文中有些结论处于笔者的猜测,如有不妥之处,欢迎指出。

源码

先看一个例子:

function Dev() {
  const [count, setCount] = React.useState(0);
  return (<div id="div">
    <button id="btn" onClick={() => {
       setCount(function add(c) { return c + 1; });
       setCount(function double(c) { return c * 2; });
       setCount(function minus(c) { return c - 1; });
      }}>click me</button>
    <div>the new text is <span>{count}</span></div>
  </div>);
}
ReactDOM.render(
  <div>
    <h1>Hello World!</h1>
    <Dev />
  </div>
  , document.getElementById('container'));

Render 阶段

先看 Render 阶段的 fiber tree 结果,如下


render 的结果

如第一章所述,在第一次 commit 之前,fiberRoot 下面的 current 还是空的,所有的 fiber 节点都在 fiberRoot.current.alternate,fiber 中的几个重要的属性如下:

  • child: 子节点,任何 fiber 最多只有一个子节点。
  • return: 父节点,出了 HostRoot 节点以外,都有 return 指向父节点。
  • sibling: 兄弟节点,单向指向下一个兄弟节点。
  • stateNode: 指向 fiber 对应的 DOM 节点。
  • tag: fiber 的标记,在此例中涉及到的 tag 有以下几个类型。
    • 3: HostRoot, fiber 树中的除了 fiberRoot 以外的根结点。
    • 5: HostComponent, 指代 DOM 树中对应可以有子节点的节点,例如 div, button 以及 span 等。
    • 6: HostText, 指代 DOM 树中的文字。
    • 0: FunctionComponent, 函数组件,本例中只有 function Dev()
  • type: fiber 的类型,如果是 HostComponent,指定对应的 DOM tag 类型,例如 div, button, span 等。如果是 FunctionComponent,指向对应的函数定义,本例中指定 function Dev()
  • flags: flags 使用字节位进行存储,所以一个 flags 中可以使用多种标记。在 render 过程中, React 给 fiber 加过的标记有:
    • 512: Snapshot,从源码中可以发现,这个标记针对于组件中含有 getSnapshotBeforeUpdate 生命周期的。不过对于 HostRoot,React 有单独的注释。因为是初次加载,暂时不涉及 update 的情况。

      // Schedule an effect to clear this container at the start of the next commit.
      // This handles the case of React rendering into a container with previous children.
      // It's also safe to do for updates too, because current.child would only be null
      // if the previous render was null (so the the container would already be empty).

    • 1: PerformedWork, 从代码注释中可以看到,这个标记是给 React DevTools 使用的。
    • 2: Placement,标记此 fiber 需要插入到 DOM 节点。

另外需要注意的是, buttononClick 属性并没有在 fiber 树中有特殊体现,只是在 DOM 节点中有属性对其进行了存储。onClick 属性也没有对 button 添加对应的事件监听。

Commit 阶段

本例中没有 useEffect 或者 useLayoutEffect 的调用,整体的 commit 阶段比较简单。

  1. commitMutationEffects,这是主要的 commit 阶段,就是将 fiber 树 commit 到 DOM 上的阶段
  2. commitMutationEffects_begin,从代码中可以看到,这个阶段是在插入或者更新之前先做一些删除工作,咱们会在后续的系列中探索这里面的工作。
  3. commitMutationEffects_complete,可以看到这是一个递归向上的过程,本例中中只执行了一次,咱们会在后续的系列中探索这里的工作。
  4. commitMutationEffectsOnFiber,这一阶段检查 fiber(HostRoot) 下的第一个子节点 fiber(HostComponent) 的 flags,发现其中有 Placement 的标记。执行 Placement 的操作
  5. commitPlacement,在此阶段执行插入的操作,会先检查此 fiber 是 HostRoot,找到其父节点对应的 containerInfor,即找到需要插入的地方。
  6. insertOrAppendPlacementNodeIntoContainer,最后执行此函数来将 fiber(HostComponent) 下的 stateNode (div) 插入到 containerInfor

总结

本节通过一个例子,了解到了 ReactDOM.render 的主要流程 rendercommit。对 fiber 的 主要结构及主要的 commit 流程有所描述。在下一节,笔者计划再次通过此例探索 React 更新时的主要流程。本节中涉及到的过程还有很多的细节未能描述,会在后续的系列中继续探究。

相关文章

网友评论

      本文标题:React 源码探源 2 Mount 复杂的例子

      本文链接:https://www.haomeiwen.com/subject/aaiyyltx.html