React:绘制思维模型

2025-06-04

React:绘制思维模型

无论你使用 React 多年,还是刚刚入门,在我看来,拥有一个实用的思维模型是提升使用 React 信心的最快途径。
拥有良好的思维模型,你就能直观地理解复杂的问题和设备解决方案,这比你一步步寻找解决方案要快得多。

免责声明:本文由一位初学者撰写,旨在梳理他刚学到的新概念。您也可以在我的博客上阅读,我在那里解释了一些帮助我解决问题和应对复杂性的思维模型。

为什么心智模型很重要?

当我开始搭建网站时,我很难理解它的工作原理。用 WordPress 搭建博客网站很容易,但我对主机托管、服务器、DNS、证书等等一无所知。

随着我阅读文章并尝试各种方法,我开始掌握这个系统,了解它的工作原理,直到最终“恍然大悟”,我感觉能够自如地使用它。我的大脑已经围绕这个系统建立了一个心理模型,我可以用它来操作它。

如果有人能把他们的思维模型讲解给我听,我就能理解得更快。在这里,我将解释(并展示)我在 React 中使用的思维模型。这将帮助你更好地理解 React,并让你成为更优秀的开发者。

React 心智模型

React 帮助我们比以往更轻松地构建复杂的交互式 UI。它还鼓励我们以特定的方式编写代码,指导我们创建更易于导航和理解的应用程序。

看着 React 徽标时,头脑中的抽象模型
React 本身是一个心理模型,其核心是一个简单的想法:封装依赖于类似逻辑和 UI 的应用程序中的部分,React 将确保该部分始终保持最新。

无论你使用 React 多年,还是刚刚入门,清晰的思维模型都是让你充满自信地使用它的最佳途径。因此,为了将我的思维模型传递给你,我将从第一性原理开始,并在此基础上进行构建。

它的功能始终如一

让我们首先对 JavaScript 和 React 的基本构建块进行建模:函数。

React 组件只是一个函数

包含其他组件的组件是函数调用其他函数。Prop
是函数的参数。
这些都被 React 使用的标记语言 JSX 隐藏了起来。剥离 JSX,React 就是一堆互相调用的函数。JSX 本身就是一种实用的思维模型,它使 React 的使用更加简单直观。

让我们分别看一下每个部分。

组件是返回 JSX 的函数

React 与 JSX(JavaScript XML)结合使用,JSX 是一种能够以 HTML 形式编写代码,同时又能充分发挥 JavaScript 的强大功能的方法。JSX 提供了一种非常实用的思维模型,能够以直观的方式使用嵌套函数。

让我们先忽略类组件,专注于更常见的函数式组件。函数式组件是一个行为与其他 JavaScript 函数完全相同的函数。React 组件始终返回 JSX 代码,然后执行该代码并将其转换为 HTML。

简单的 JSX 如下所示:

const Li = props => <li {...props}>{props.children}</li>;

export const RickRoll = () => (
  <div>
    <div className='wrapper'>
      <ul>
        <Li color={'red'}>Never give you up</Li>
      </ul>
    </div>
  </div>
);

通过 Babel 编译成纯 JavaScript:

const Li = props => React.createElement('li', props, props.children);

export const RickRoll = () =>
  React.createElement(
    'div',
    null,
    React.createElement(
      'div',
      {
        className: 'wrapper',
      },
      React.createElement(
        'ul',
        null,
        React.createElement(
          Li,
          {
            color: 'red',
          },
          'Never give you up',
        ),
      ),
    ),
  );

如果您发现此代码难以理解,那么您并不孤单,并且您会理解为什么 React 团队决定使用 JSX。

现在,请注意每个组件都是一个调用另一个函数的函数,并且每个新组件都是 React.createElement 函数的第三个参数。无论何时编写组件,记住它是一个普通的 JavaScript 函数都会很有用。

React 的一个重要特性是,一个组件可以有多个子组件,但只能有一个父组件。我之前一直对此感到困惑,直到我意识到这和 HTML 的逻辑是一样的:每个元素都必须位于其他元素内部,并且可以有多个子组件。你可以在上面的代码中注意到这一点,只有一个父 div 包含所有子组件。

组件的 props 与函数的参数相同

使用函数时,我们可以使用参数与该函数共享信息。在 React 组件中,我们将这些参数称为 props(有趣的是,我很久以前才意识到 props 是 properties 的缩写)。

在底层,props 的行为与函数参数完全相同,不同之处在于我们通过 JSX 更好的界面与它们交互,并且 React 为 props 提供了额外的功能,例如 children。

围绕函数创建思维模型

利用这些知识,让我们构建一个心理模型来直观地理解函数!

当我思考一个函数时,我会把它想象成一个盒子,每当它被调用时,它就会做一些事情。它可以返回值,也可以不返回:

function sum(a, b) {
  return a + b;
}

console.log(sum(10, 20)); // 30

function logSum(a, b) {
  console.log(a + b); // 30
}

由于组件是一种奇特的功能,因此它也使组件成为一个盒子,并以 props 作为盒子创建输出所需的成分。

当一个组件被执行时,它会运行其所有逻辑(如果有),并执行其 JSX 代码。所有标签都会转换为 HTML,所有组件都会被执行,这个过程不断重复,直到到达子组件链中的最后一个组件。

由于一个组件可以有多个子组件,但只有一个父组件,因此我将多个组件想象成一组盒子,一个盒子套着另一个盒子。每个盒子必须包含在一个更大的盒子中,并且里面可以包含许多更小的盒子。

但是,如果不理解一个盒子如何与其他盒子交互,那么代表一个组件的盒子的心理模型就是不完整的。

如何思考闭包

闭包是 JavaScript 的核心概念。它使 JavaScript 能够实现复杂的功能,理解闭包对于理解 React 至关重要。

它们也是新手最难掌握的功能之一,因此,我不会解释技术细节,而是展示我对闭包的心理模型。

闭包的基本描述是,它是一个函数。我把它想象成一个盒子,它阻止里面的东西溢出,同时允许外面的东西进入,就像一个半透性的盒子。但是溢出到哪里呢?

虽然闭包本身是一个盒子,但任何闭包都会位于更大的盒子内,最外层的盒子是 Window 对象。

一个描述 JavaScript 闭包的思维模型的框,显示窗口、脚本和 React 应用程序
窗口对象封装了其他所有内容

但是闭包是什么?

closure是 JavaScript 函数的一个特性。如果你使用函数,那么你就在使用闭包。

正如我之前提到的,函数是一个盒子,而闭包也是一个盒子。考虑到每个函数内部可以包含许多其他函数,闭包就是指函数能够使用其外部信息,同时又能防止其内部信息“溢出”或被外部函数使用的能力。

就我的思维模型而言:我将函数想象成一个个盒子套着一个盒子,每个小盒子都能看到外层盒子(或父盒子)的信息,但大盒子却看不到小盒子的信息。这就是我能给出的对闭包最简单、最准确的解释。

闭包很重要,因为它们可以被利用来创建一些强大的机制,而 React 充分利用了这一点。

React 中的闭包

每个 React 组件本身也是一个闭包。在组件内部,你只能将 props 从父组件向下传递给子组件,父组件无法看到子组件内部的内容。这个特性旨在简化应用的数据流追踪。为了找到数据的来源,我们通常需要沿着组件树向上查找是哪个父组件向下传递数据的。

React 中闭包的一个很好的例子是通过子组件更新父组件的状态。你可能做过这样的事情,却没有意识到自己在使用闭包。

首先,我们知道父组件无法直接访问子组件的信息,但子组件可以访问父组件的信息。因此,我们通过 props 将这些信息从父组件传递给子组件。在本例中,这些信息以函数的形式存在,用于更新父组件的状态。

const Parent = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      The count is {count}
      <div>
        <ChildButtons onClick={setCount} count={count} />
      </div>
    </div>
  );
};

const ChildButtons = props => (
  <div>
    <button onClick={() => props.onClick(props.count + 1)}>
      Increase count
    </button>
    <button onClick={() => props.onClick(props.count - 1)}>
      Decrease count
    </button>
  </div>
);

当按钮发生 onClick 时,它将执行从 props props.onClick 接收的函数,并使用 props.count 更新值。

这里的要点在于我们通过子组件更新父组件状态的方式,在本例中就是 props.onClick 函数。之所以有效,是因为该函数是在 Parent 组件的作用域内(也就是其闭包内)声明的,因此它可以访问父组件的信息。一旦该函数在子组件中被调用,它仍然位于同一个闭包中。

这可能有点难以理解,所以我把它想象成闭包之间的“隧道”。每个闭包都有自己的作用域,但我们可以创建一个连接两者的单向通信隧道。

一旦我们理解了闭包如何影响我们的组件,我们就可以采取下一个重要步骤:React 状态。

将 React 的状态融入我们的思维模型

React 的理念很简单:它负责处理何时以及如何渲染元素,而开发者则负责控制渲染的内容。状态是我们决定渲染什么的工具。

当状态发生变化时,其组件会渲染,并重新执行其中的所有代码。我们这样做是为了向用户显示新的、更新的信息。

在我的思维模型中,状态就像盒子里的一个特殊属性。它独立于盒子内部发生的所有其他事情。它会在首次渲染时获得一个默认值,并始终保持最新值。

每个变量和函数在每次渲染时都会创建,这意味着它们的值也是全新的。即使变量的值从未改变,每次也会重新计算并重新赋值。状态则不然,它只有在通过事件请求更改时才会更改set state

状态遵循一个简单的规则:每当它发生变化时,它都会重新渲染组件及其子组件。属性遵循相同的逻辑,如果属性发生变化,组件将重新渲染。然而,我们可以通过修改它来控制状态,而属性则更加静态,通常会随着状态的变化而变化。

渲染思维模型:理解 React 的魔力

我认为渲染是 React 中最令人困惑的部分,因为渲染过程中发生的很多事情有时通过查看代码并不容易发现。因此,清晰的思维模型至关重要。

我设想的渲染方式是双重的:第一次渲染使盒子生效,也就是初始化状态。第二次渲染是重新渲染,盒子被回收利用,大部分内容都是全新的,但一些重要的元素,也就是状态,仍然保留。

每次渲染时,组件内部的所有内容都会被创建,包括变量和函数。这就是为什么我们可以使用变量来存储计算结果,因为它们每次渲染时都会重新计算。这也是为什么函数作为值不可靠的原因,因为它们的引用(函数本身的值)每次渲染时都会不同。

const Thumbnail = props => (
  <div>
    {props.withIcon && <AmazingIcon />}
    <img src={props.imgUrl} alt={props.alt} />
  </div>
);

上述代码会根据组件接收的 props 给出不同的结果。React 必须在每次 props 更改时重新渲染,是因为它希望让用户及时了解最新信息。

但是,状态在重新渲染时不会改变,其值保持不变。这就是为什么盒子会被“回收”,而不是每次都创建一个全新的盒子。React 内部会跟踪每个盒子,并确保其状态始终一致。这就是 React 知道何时更新组件的方式。

通过想象一个盒子被回收,我就能理解它里面发生了什么。对于简单的组件来说,这很容易理解,但组件越复杂,接收的 props 就越多,维护的状态就越多,清晰的思维模型就越有用。

完整的 React 思维模型:将所有内容整合在一起。

现在我已经分别解释了这个谜题的各个部分,让我们把它们放在一起。这是我对 React 组件的完整思维模型,直接将我的想象转化为文字。

我将 React 组件想象成一个盒子,其内部包含所有信息,包括其子组件(更多的是盒子)。

就像现实世界中的盒子一样,它里面可以包含其他盒子,而这些盒子又可以包含更多的盒子。因此,每个盒子/组件必须有一个父级,而一个父级可以有多个子级。

这些盒子是半透性的,这意味着它们不会向外泄漏任何东西,但可以像使用自己的信息一样使用来自外部的信息。我设想用这种方式来表示 JavaScript 中闭包的工作原理。

在 React 中,组件之间共享信息的方式称为 props,同样的想法适用于函数,然后它被称为参数,它们以相同的方式工作,但使用不同的语法。

在组件内部,信息​​只能从父组件向下传递到子组件。换句话说,子组件可以访问父组件的数据和状态,但反之则不行。我们共享这些信息的方式是通过 props。

我把这种定向的信息共享想象成一个个盒子套着一个盒子。最里面的盒子可以吸收父级的数据。

React 组件间数据共享的思维模型,可视化为信息向下流动,即数据从父组件共享到子组件。
不过,首先必须创建盒子,这发生在渲染时,此时会为 state 赋默认值,并且像函数一样,组件内的所有代码都会被执行。在我的思维模型中,这相当于创建了盒子。

后续渲染(或重新渲染)会再次执行组件中的所有代码,重新计算变量、重新创建函数等等。除了状态之外,所有内容在每次渲染时都是全新的。状态的值在渲染过程中保持不变,并且仅通过 set 方法进行更新。

在我的心理模型中,我将重新渲染视为回收盒子,因为其中大部分都是重新创建的,但由于 React 跟踪组件的状态,它仍然是同一个盒子。

当一个盒子被回收时,它里面的所有盒子(也就是它的子盒子)也会被回收。这可能是因为组件的状态被修改了,或者 props 发生了变化。

请记住,状态或属性的改变意味着用户看到的信息已经过时,而 React 始终希望保持 UI 更新,因此它会重新渲染必须显示新数据的组件。

结论

通过运用这些思维模型,我使用 React 时更加自信。它们帮助我将原本错综复杂的代码转化为清晰的思维导图。它还揭开了 React 的神秘面纱,让我能够更加轻松地使用它。

一旦你开始理解 React 背后的核心原理并创建一些方法来想象你的代码如何工作,React 就不会那么复杂。

希望这篇文章对你有所帮助,阅读和写作都一样愉快!我意识到我对 React 的理解是直观的,而将这种理解转化为文字却颇具挑战性。

文章来源:https://dev.to/carter/react-painting-a-mental-model-1hd8
PREV
向 Next.js 网站添加 lang 属性
NEXT
使用 Tailwind CSS 快速进行 UI 开发