React技术细节手册 - 揭秘 React 内部架构

515 阅读4分钟

写在前头

本文是 React 技术细节手册的另一部分, 本文旨在揭秘 React 内部架构的设计细节, 如果你知晓 Fiber, React, ReactDom, ReactHook, 以及关于 workInProgress, 时间调度等诸多概念, 但是却困惑于他们之间的关系, 或许本文可以帮你解惑.

React Hook 的运行原理和让人困惑的秘密 和上一篇文章一样, 本文也存在后续修订的可能性, 其讨论的架构细节都是建立在 16.13.1 版本上的, 后续可能因为版本变化等诸多原因而修订

那让我们从第一个问题开始, 逐步揭开 React 内部的架构设计的面纱

正文

Fiber 是什么?

这部分并不会展开 Fiber 的内部细节, 如果你对这个感兴趣, 可以在掘金上搜索相关的文章

Fiber 是 React 内部架构中负责调度的基础设施, 用一句话概括Fiber 是一个有链表构建的虚拟堆栈, 其包含一个 current 当前堆栈, 和基于 current 构建的 workInProgress 堆栈, FiberNode 是堆栈可操作的最小单位, 基于 Fiber 你可以在 JSRunTime 中构建自己的渲染框架或者其他工作框架, 让 Fiber 帮你优化代码的执行效率, 关于这部分可以查阅相关资料, 掘金上也有关于 Fiber 内部的运行细节, 后续我可能也会就 Fiber 作为另一篇文章的主题.

便于阅读, workInProgress 简称 wip, 这里的 Fiber 指的是经过 ReactDom 配置过得 Fiber, 并不是最基础的那个.

React 和 Fiber 之间如何产生联系 ?

大部分人应该都知道在浏览器环境中运行的 React 分为两部分, React 或者叫 ReactCore 和 ReactDom, 其中 ReactDom 是通过配置 Fiber 构建出来的库, 类似的还有 ReactArt 等, 先来看一行代码

ReactDOM.render(React.createElement(Counter, { initialCount: 1 }, null),
    document.getElementById('root')
);

大家应该对 createElement 不会太陌生, 事实上在 ReactCore 内部是各种各样的 element 构建 API, 关于这部分的细节可能会比较枯燥, 因为主要是处理各种各样的创建节点的操作, 你可以将 ReactCore 理解成一个 Element 工厂, 用来在浏览器环境中创建各种各样的 Element, 所以 ReactCore 在创建 Element 的过程中又是如何和 Fiber 建立联系从而通过 Fiber 去进行 Reconciliation 的呢? 先来看看 ReactCore 对于 Element 的结构定义

这里的 ReactCore 就是平时用的 React

    var element = {
      // This tag allows us to uniquely identify this as a React Element
      ?typeof: REACT_ELEMENT_TYPE,
      // Built-in properties that belong on the element
      type: type,
      key: key,
      ref: ref,
      props: props,
      // Record the component responsible for creating this element.
      _owner: owner
    };

关键就是 _owner 这个内部属性, 通过 _owner, 每一个 ReactElement 就和一个 FiberNode 产生了映射, 简单用图表示下

ReactCore                   Fiber
[ ReactElement ] _owner--> [ FiberNode ]

而在 ReactDom 中通过 Fiber 经历了一系列 Reconciliation 后, wip 上的 stateNode 会被最终用于插入到真实 Dom 中, 于是我们将上面的图可以变成

关于 stateNode, 可以参考 Fiber 相关文章或者在后续的文章中讨论

ReactCore                   Fiber(ReactDom)               Browser
[ ReactElement ] _owner--> [ FiberNode ] <.stateNode> --> [ RealDom ]

因为目前 React 使用了一些例如变量注入等方式, 所以 Fiber 还不是一个真正意义上架构独立的部分, 在这里 ReactDom 可以看成是一个专为浏览器环境构建的 Fiber 版本

不过眼尖的读者会发现上面的图, 只表示了一种静态的架构关系, 毕竟 Fiber 是一个链表, 它是会移动的, 那当 Fiber 开始运行 ReactCore 又如何和它保持联系呢?

答案就是一个运行时的全局对象指针 ReactCurrentOwner.current

ReactCurrentOwner$1.current = workInProgress;

在 Fiber 运行的过程中, 会保持该指针对 workInProgress 的引用, 以便在 ReactCore 中获取 Fiber 的工作进度, 然后进行一些处理, 因此我们可以再修改下上面的架构图


ReactCore                   Fiber(ReactDom)               Browser
[ ReactElement ] _owner--> [ FiberNode ] <.stateNode> --> [ RealDom ]
runtime: ↑ ReactCurrentOwner.current ↑   (Reconciliation)

Hook 在哪? 有什么不同么?

上面的图基本说明了没有 Hook 之前的版本, 那么再加入 Hook 之后架构又发生了什么变化呢?

别急, 未完待续....

写在后头

希望通过对 React 内部架构的一个梳理, 让各位对 React 16 版本有一个系统性的认识, 从而理解 Fiber ReactCore, ReactDom 之间的关系, 也希望通过对 Hook 加入这套架构之后的分析, 能引发大家对前端架构的思考🤔, 比如 React 目前这种内部架构的优缺点? 后续会如何演进, 以及在加入最新的 concurrent 模式后, 架构又会发生哪些变化呢?

架构设计是一种更高抽象的全局性的系统设计, 利用好对架构的理解有助于我们拆解复杂系统, 对局部系统进行分析, 同时也可以分析其架构的合理性, 扩展性以及潜在的问题等, 如果对前端架构感兴趣的可以加我微信畅聊哈