探索 React Fiber tree

React将其呈现树的内部表示形式存储为React Fiber(v16.0+)。它为我们渲染的每个JSX元素创建一个Fiber节点,然后将其连接到其父节点、子节点和同级节点,以创建完整的树形结构。

我们将这个连接的树形结构称为Fiber树。

简单来说,你在React调试器的组件选项卡中找到关于它的大部分信息。

需要注意的是,在Fiber树中,每个父节点都包含一个指向其第一个(最左侧)子节点的链接,但不包含整个子节点数组。子节点反过来链接到它的兄弟节点。
可以将其视为一个链接列表,或者正式地称为左孩子右兄弟树。
这是一个简化的表示(右图),它们还包含属性,以便引用回父级(return属性)和状态/属性(memoizedState,memoizedProps)。它还会引用回实际对应的DOM节点。

前期准备

需要注意的是,内部的Fiber数据结构可能会随着时间而发生变化。即使在v16引入Fiber之后,这些规则在小版本中也可能略有改变。这在React 16.9(react-dom同样如此)中同样存在。
本文记录了我在探索Fiber结构时所能理解的一切。Nitin关于自定义渲染器的文章详细介绍了它(请查找“Fiber”部分)。将其视为一篇有趣的阅读材料,了解一些有趣的知识,并可能基于此制作Demo项目,但不是简单的应用到生产。

Fiber的类型

该结构保持不变,但某些属性(的类型)的值会根据正在呈现的元素略有不同。

  • 1、类组件
  • 2、函数组件
  • 3、固有元素(例如JSX中的
  • 4、文本元素(例如JSX中的
    Hello
    - 仅为文本部分)
    对于每种类型,我们将查看调试器视图与内部对象结构的比较,并解释结构的各个部分。
    稍后我将尝试添加一些“自己尝试”链接。

    固有元素和文本元素


重要属性:

  • type → 元素的类型,例如“div”
  • child、sibling → 连接到这些的fiber节点。如果不存在,则为null。
  • return → 连接到父节点的链接。
  • stateNode → 链接到在DOM(浏览器)中创建的div。这是从fiber树到DOM树的唯一链接。
    文本元素类似,只是它们没有子元素或类型(两者都为null)。stateNode链接回DOM中的TextNode。

函数组件

Button是个函数组件
image.png

属性:

  • type → 函数组件的引用
  • stateNode → 对于此类型,它始终为null

    类组件

    Task是个类组件


    属性:
  • type → 类组件的引用
  • stateNode → stateNode链接回其对应的类实例。每当我们渲染一个类时,React在内部创建这个类的实例。实际上,你可以访问该实例的this,并且可以检查实例属性、调用方法等。

技巧:在React调试器中选择节点(针对类组件)会将其实例引用设置为$r。在控制台中尝试。

寻找根结点

通常,查找根fiber节点是第一步。你需要找到它,以便遍历并到达其他fiber节点。

有两种方法可以得到它:

  • A. 从根DOM节点 -读取 rootNode._reactRootContainer._internalRoot.current
  • B. 从ReactDOM.render的输出 -ReactDOM.render目前返回根fiber节点。
    稍后可能会更改,渲染也可能变成异步。

应用案例

这是最困难的部分。希望提供一些指引,说明可能存在的用例(尽管它们有些牵强)。这将引导读者在他们的上下文中提出一些用例建议。

A. 查找并突出显示组件渲染的DOM节点

B. 测试选择DOM节点 - 这只是前面一点的扩展。
通常,在端到端测试期间,我们通过类名和ID选择DOM节点。要想得到正确的选择器稍微有些麻烦:

  • a. 由于 class 名称会被混淆,所以要得到正确的选择器稍微有些麻烦,
  • b. 如果某些元素不需要自定义样式,则类名经常会丢失(更像是在React中不需要),
  • c. 所有这些都必须考虑到整个应用程序的范围。

如果你能够:
a. 首先缩小范围,只定位到你想查看的组件
b. 然后编写一个仅在该组件内部起作用的选择器

注意:这在与测试框架集成时稍微有些棘手,因为按ID/类查找是内置的 locateStrategy 和webdriver规范的一部分。添加新的可能需要一些思考。

C. 遍历Fiber节点 —
你可以遍历这些节点并将它们序列化并记录到控制台中。

相关链接

  • Resq
    • Resq是webdriver.io的一部分,用于按名称选择组件的现有项目。
    • 具有一些缺点,例如强制将所选节点和子树转换为其自己的简化结构。
  • React-fiber-traverse
    • 遍历工具,可以查找节点并使用类似于 CSS 的选择器进行匹配。
    • 目前还不太稳定。

      未来的思考

      这也涉及到自定义渲染器。你可以通过 internalInstanceHandle 访问相同的对象。Nitin关于自定义渲染器的文章详细解释了这一点。在这里我们只谈到了React-DOM作为渲染器,但是如果其他渲染器以某种方式允许访问根节点,那么这个结构应该是类似的。
      它也可以通过调试器访问。

      结束语

      React Fiber 是一个有趣的实现细节需要了解。由于其内部性质和可能的破坏性变化,投入时间了解它可能并没有太多意义。但它也是一种方便的方式,可以在组件之间“查找” React 树中的某些信息。它在开发和生产构建中都可以工作。
      也许为更复杂的用例添加自定义调试器是可行的方法。