理解你的 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作为子组件props,因此它将Copyright渲染为子组件,但不会导入模块。

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

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

总结

  • 树是表示实体之间关系的一种常用方法。它们常用于建模UI。
  • 渲染树表示单个渲染过程中React组件之间的嵌套关系。
  • 使用条件渲染,渲染树在不同的渲染过程中可能会发生变化。使用不同的prop值,组件可能会渲染不同的子组件。
  • 渲染树有助于识别顶级组件和叶子组件。顶级组件会影响其下方所有组件的渲染性能,而叶子组件则经常被重新渲染。识别它们对于理解和调试渲染性能非常有用。
  • 依赖树表示React应用程序中的模块依赖关系。
  • 依赖树由构建工具用来捆绑交付应用程序所需的代码。
  • 依赖树有助于调试大型捆绑包大小(这些大小会减慢绘制速度),并为优化捆绑的代码提供机会。