将您的 UI 理解为树结构

您的 React 应用正在成形,许多组件彼此嵌套。React 如何跟踪应用的组件结构?

React 和许多其他 UI 库将 UI 建模为树结构。将您的应用视为树结构有助于理解组件之间的关系。这种理解将帮助您调试未来的概念,如性能和状态管理。

您将学习

  • React 如何“看待”组件结构
  • 什么是渲染树以及它的用途
  • 什么是模块依赖树以及它的用途

您的 UI 作为树结构

树是项目之间的一种关系模型,UI 通常使用树结构来表示。例如,浏览器使用树结构来模拟 HTML(DOM)和 CSS(CSSOM)。移动平台也使用树来表示它们的视图层次结构。

Diagram with three sections arranged horizontally. In the first section, there are three rectangles stacked vertically, with labels 'Component A', 'Component B', and 'Component C'. Transitioning to the next pane is an arrow with the React logo on top labeled 'React'. The middle section contains a tree of components, with the root labeled 'A' and two children labeled 'B' and 'C'. The next section is again transitioned using an arrow with the React logo on top labeled 'React DOM'. The third and final section is a wireframe of a browser, containing a tree of 8 nodes, which has only a subset highlighted (indicating the subtree from the middle section).
Diagram with three sections arranged horizontally. In the first section, there are three rectangles stacked vertically, with labels 'Component A', 'Component B', and 'Component C'. Transitioning to the next pane is an arrow with the React logo on top labeled 'React'. The middle section contains a tree of components, with the root labeled 'A' and two children labeled 'B' and 'C'. The next section is again transitioned using an arrow with the React logo on top labeled 'React DOM'. The third and final section is a wireframe of a browser, containing a tree of 8 nodes, which has only a subset highlighted (indicating the subtree from the middle section).

React 使用您的组件创建一个 UI 树。在这个例子中,UI 树随后被用来渲染到 DOM。

像浏览器和移动平台一样,React 也使用树结构来管理和建模 React 应用中组件之间的关系。这些树是理解数据如何流经 React 应用以及如何优化渲染和应用大小的有用工具。

渲染树

组件的一个主要特性是能够组合其他组件的组件。当我们 嵌套组件 时,我们引入了父组件和子组件的概念,其中每个父组件本身也可能是另一个组件的子组件。

当我们渲染一个 React 应用时,我们可以用一棵树来模拟这种关系,称为渲染树。

这是一个渲染励志名言的 React 应用。

import FancyText from './FancyText';
import InspirationGenerator from './InspirationGenerator';
import Copyright from './Copyright';

export default function App() {
  return (
    <>
      <FancyText title text="Get Inspired App" />
      <InspirationGenerator>
        <Copyright year={2004} />
      </InspirationGenerator>
    </>
  );
}

Tree graph with five nodes. Each node represents a component. The root of the tree is App, with two arrows extending from it to 'InspirationGenerator' and 'FancyText'. The arrows are labelled with the word 'renders'. 'InspirationGenerator' node also has two arrows pointing to nodes 'FancyText' and 'Copyright'.
Tree graph with five nodes. Each node represents a component. The root of the tree is App, with two arrows extending from it to 'InspirationGenerator' and 'FancyText'. The arrows are labelled with the word 'renders'. 'InspirationGenerator' node also has two arrows pointing to nodes 'FancyText' and 'Copyright'.

React 创建了一个由渲染的组件组成的渲染树,即 UI 树。

从示例应用中,我们可以构建上面的渲染树。

树由节点组成,每个节点代表一个组件。AppFancyTextCopyright 等等都是我们树中的节点。

React 渲染树中的根节点是应用的 根组件。在本例中,根组件是 App,它是 React 渲染的第一个组件。树中的每个箭头都从父组件指向子组件。

深入探讨

HTML 标签在渲染树中的哪里?

您会注意到在上面的渲染树中,没有提到每个组件渲染的 HTML 标签。这是因为渲染树只由 React 组件 组成。

React 作为一个 UI 框架,与平台无关。在 react.dev 上,我们展示了渲染到 Web 的示例,它使用 HTML 标记作为其 UI 原语。但 React 应用程序也可以渲染到移动或桌面平台,它们可能使用不同的 UI 原语,例如 UIViewFrameworkElement

这些平台 UI 原语不是 React 的一部分。无论你的应用程序渲染到哪个平台,React 渲染树都可以提供对我们 React 应用程序的洞察。

渲染树表示 React 应用程序的单次渲染过程。通过 条件渲染,父组件可以根据传递的数据渲染不同的子组件。

我们可以更新应用程序以有条件地渲染励志名言或颜色。

import FancyText from './FancyText';
import InspirationGenerator from './InspirationGenerator';
import Copyright from './Copyright';

export default function App() {
  return (
    <>
      <FancyText title text="Get Inspired App" />
      <InspirationGenerator>
        <Copyright year={2004} />
      </InspirationGenerator>
    </>
  );
}

Tree graph with six nodes. The top node of the tree is labelled 'App' with two arrows extending to nodes labelled 'InspirationGenerator' and 'FancyText'. The arrows are solid lines and are labelled with the word 'renders'. 'InspirationGenerator' node also has three arrows. The arrows to nodes 'FancyText' and 'Color' are dashed and labelled with 'renders?'. The last arrow points to the node labelled 'Copyright' and is solid and labelled with 'renders'.
Tree graph with six nodes. The top node of the tree is labelled 'App' with two arrows extending to nodes labelled 'InspirationGenerator' and 'FancyText'. The arrows are solid lines and are labelled with the word 'renders'. 'InspirationGenerator' node also has three arrows. The arrows to nodes 'FancyText' and 'Color' are dashed and labelled with 'renders?'. The last arrow points to the node labelled 'Copyright' and is solid and labelled with 'renders'.

通过条件渲染,在不同的渲染过程中,渲染树可能会渲染不同的组件。

在本例中,根据 inspiration.type 的值,我们可能会渲染 <FancyText><Color>。每次渲染过程的渲染树可能都不同。

尽管渲染树在不同的渲染过程中可能会有所不同,但这些树通常有助于识别 React 应用程序中的*顶级*和*叶子组件*。顶级组件是最靠近根组件的组件,会影响其下方所有组件的渲染性能,并且通常包含最复杂的逻辑。叶子组件位于树的底部,没有子组件,并且经常被重新渲染。

识别这些类别的组件有助于理解应用程序的数据流和性能。

模块依赖树

在 React 应用程序中,可以用树来建模的另一种关系是应用程序的模块依赖关系。当我们将组件和逻辑拆分到不同的文件中时,我们就创建了JS 模块,我们可以在其中导出组件、函数或常量。

模块依赖树中的每个节点都是一个模块,每个分支表示该模块中的一个 import 语句。

如果我们以之前的 Inspirations 应用程序为例,我们可以构建一个模块依赖树,或简称为依赖树。

A tree graph with seven nodes. Each node is labelled with a module name. The top level node of the tree is labelled 'App.js'. There are three arrows pointing to the modules 'InspirationGenerator.js', 'FancyText.js' and 'Copyright.js' and the arrows are labelled with 'imports'. From the 'InspirationGenerator.js' node, there are three arrows that extend to three modules: 'FancyText.js', 'Color.js', and 'inspirations.js'. The arrows are labelled with 'imports'.
A tree graph with seven nodes. Each node is labelled with a module name. The top level node of the tree is labelled 'App.js'. There are three arrows pointing to the modules 'InspirationGenerator.js', 'FancyText.js' and 'Copyright.js' and the arrows are labelled with 'imports'. From the 'InspirationGenerator.js' node, there are three arrows that extend to three modules: 'FancyText.js', 'Color.js', and 'inspirations.js'. The arrows are labelled with 'imports'.

Inspirations 应用程序的模块依赖树。

树的根节点是根模块,也称为入口文件。它通常是包含根组件的模块。

与同一个应用程序的渲染树相比,它们有相似的结构,但也有一些显著的区别

  • 构成树的节点表示的是模块,而不是组件。
  • 非组件模块(如 inspirations.js)也表示在这棵树中。渲染树只封装组件。
  • Copyright.js 出现在 App.js 下面,但在渲染树中,组件 Copyright 出现在 InspirationGenerator 的子级中。这是因为 InspirationGenerator 接受 JSX 作为子组件属性,因此它将 Copyright 渲染为子组件,但没有导入该模块。

依赖树有助于确定运行 React 应用程序所需的模块。在构建用于生产环境的 React 应用程序时,通常会有一个构建步骤,将所有必要的 JavaScript 打包到客户端。负责此操作的工具称为打包器,打包器将使用依赖树来确定应包含哪些模块。

随着应用程序的增长,通常打包大小也会增加。较大的打包大小对于客户端来说下载和运行成本很高。较大的打包大小可能会延迟 UI 的绘制时间。了解应用程序的依赖树可能有助于调试这些问题。

总结

  • 树是表示实体之间关系的常用方式。它们通常用于对 UI 进行建模。
  • 渲染树表示在单次渲染过程中 React 组件之间的嵌套关系。
  • 通过条件渲染,渲染树在不同的渲染过程中可能会发生变化。根据不同的属性值,组件可能会渲染不同的子组件。
  • 渲染树有助于识别顶级组件和叶子组件。顶级组件会影响其下方所有组件的渲染性能,叶子组件通常会频繁地重新渲染。识别它们有助于理解和调试渲染性能。
  • 依赖树表示 React 应用程序中的模块依赖关系。
  • 构建工具使用依赖树将必要的代码打包到应用程序中。
  • 依赖树有助于调试导致绘制时间变慢的大型打包,并为优化打包代码提供机会。