My App

核心 React 循环

核心 React 循环

所以,我们已经看到,当我们通过调用设置函数 ( setCount ) 更新状态变量时,UI 会更新。但是,这究竟是如何工作的呢?

这个问题直接切入了 React 的核心。毕竟,这个库的名字字面上就是为了说明它如何对状态变化作出反应!

这是一个我们将在整个课程中不断提到的问题,因为我们正在构建对 React 运作方式的认知模型。但让我们看看是否可以开始回答它!

让我们继续使用我们的“计数器”例子:

function Counter() {
  const [count, setCount] = React.useState(0);
 
  return <button onClick={() => setCount(count + 1)}>Value: {count}</button>;
}

作为一个简要回顾,我们来讨论一下当这个组件第一次被渲染时发生了什么。

我们的 Counter 函数返回一堆 JSX。让我们用纯 JavaScript 重写它,这样我们就可以看到这里到底发生了什么:

function Counter() {
  const [count, setCount] = React.useState(0);
 
  return React.createElement(
    "button",
    { onClick: () => setCount(count + 1) },
    "Value: ",
    count,
  );
}

当这段代码运行时, React.createElement 生成一个 React 元素,它是一个普通的 JavaScript 对象。它看起来像这样:

{
  type: 'button',
  key: null,
  ref: null,
  props: {
    onClick: () => setCount(count + 1),
    children: 'Value: 0',
  },
  _owner: null,
  _store: { validated: false }
}

正如我们在上一个模块中所学到的,React 元素本质上是我们想要的 UI 的描述。我们在这里说,我们想要一个包含文本“Value: 0”的按钮。

我们可以将这个 JavaScript 对象可视化为以下 HTML 代码片段:

<button>Value: 0</button>

我们的 React 元素,即那个 JavaScript 对象,正在描述这个 DOM 结构。React 读取这个描述,并将其转换成真实的东西。它创建一个 <button> DOM 节点并将其附加到页面上。

我没有在这个小示例中展示 onClick 处理程序,但它确实是这个过程的一部分。当 React 创建并注入 <button> DOM 节点时,它会附加我们的处理函数。

现在,让我们想想当点击这个按钮时会发生什么。

将调用 setCount 函数,并传入一个新值。 count 将从 0 增加到 1。

每当状态变量更新时,它将触发重新渲染。React 将再次调用 Counter 函数。这会创建一个全新的 React 元素,即我们想要的用户界面的新描述。

新的 React 元素描述了这个 DOM 结构:

<button>Value: 1</button>

(我在这里以 HTML 的形式展示,因为这样更容易演示,但实际上,React 处理的是描述这种标记的 JavaScript 对象。)

每次渲染就像是拍摄快照。我们生成一个描述,显示用户界面应如何显示,基于组件的属性/状态。这就像是一张捕捉某个时刻事物样子的照片。

因此,React 有两个快照:

<button>Value: 0</button>
<button>Value: 1</button>

用户点击了按钮,生成了这个第二个快照。React 现在必须弄清楚如何更新 DOM,以便与这个最新快照匹配。

你知道那些游戏吗?在游戏中,你会看到两幅稍有不同的图片,而你需要找出它们之间的差异

spot-the-differences

React 本质上必须进行这种游戏,在两个快照之间寻找变化。

这个过程被称为和解。使用高级优化算法,React 计算出发生了什么变化。它看到按钮的文本内容从 "Value: 0" 变为 "Value: 1"。

一旦 React 解决了难题并弄清楚了有什么不同,它就需要提交这些更改。它以精湛的技巧更新 DOM,注意只调整需要调整的部分。

在这种情况下,操作会类似于:

button.innerText = "Value: 1";

这是 React 的基本“流程”,核心循环。我们可以把这个序列可视化为:

react-core-loop-1

react-core-loop-2

react-core-loop-3

react-core-loop-4