你的第一个组件

组件是 React 的核心概念之一。它们是你构建用户界面 (UI) 的基础,这使得它们成为开始 React 之旅的完美起点!

你将学习

  • 什么是组件
  • 组件在 React 应用中扮演什么角色
  • 如何编写你的第一个 React 组件

组件:UI 构建块

在 Web 上,HTML 允许我们使用其内置的一组标签(如 <h1><li>)创建丰富的结构化文档。

<article>
<h1>My First Component</h1>
<ol>
<li>Components: UI Building Blocks</li>
<li>Defining a Component</li>
<li>Using a Component</li>
</ol>
</article>

此标记表示本文 <article>、其标题 <h1> 和作为有序列表的(简略)目录 <ol>。这种标记与用于样式的 CSS 和用于交互性的 JavaScript 结合在一起,构成了你在 Web 上看到的每个侧边栏、头像、模态框、下拉菜单——每个 UI 元素的基础。

React 允许你将你的标记、CSS 和 JavaScript 结合到自定义“组件”中,应用的可重用 UI 元素。你上面看到的目录代码可以转换为一个 <TableOfContents /> 组件,你可以在每个页面上渲染它。在后台,它仍然使用相同的 HTML 标签,如 <article><h1> 等。

就像 HTML 标签一样,你可以组合、排序和嵌套组件来设计整个页面。例如,你正在阅读的文档页面是由 React 组件组成的。

<PageLayout>
<NavigationHeader>
<SearchBar />
<Link to="/docs">Docs</Link>
</NavigationHeader>
<Sidebar />
<PageContent>
<TableOfContents />
<DocumentationText />
</PageContent>
</PageLayout>

随着项目的增长,你会注意到许多设计可以通过重用你已经编写的组件来组合,从而加快你的开发速度。我们上面的目录可以通过 <TableOfContents /> 添加到任何屏幕!你甚至可以使用 React 开源社区共享的数千个组件来快速启动你的项目,例如 Chakra UIMaterial UI。

定义组件

传统上,在创建网页时,Web 开发人员会标记其内容,然后通过添加一些 JavaScript 来添加交互性。当交互性在 Web 上只是一个不错的附加功能时,这非常有效。现在,许多网站和所有应用程序都期望具有交互性。React 首先考虑交互性,同时仍然使用相同的技术:React 组件是一个 JavaScript 函数,你可以在其上添加标记以下是它的样子(你可以编辑下面的示例)

export default function Profile() {
  return (
    <img
      src="https://i.imgur.com/MK3eW3Am.jpg"
      alt="Katherine Johnson"
    />
  )
}

以下是构建组件的方法

步骤 1:导出组件

前缀 export default标准的 JavaScript 语法(并非 React 独有)。它允许你标记文件中主函数,以便之后从其他文件中导入它。(更多关于导入的信息请参见 导入和导出组件!)

步骤 2:定义函数

使用 function Profile() { } 你定义了一个名为 Profile 的 JavaScript 函数。

陷阱

React 组件是普通的 JavaScript 函数,但是它们的名称必须以大写字母开头,否则将无法工作!

步骤 3:添加标记

该组件返回一个带有 srcalt 属性的 <img /> 标签。<img /> 的写法像 HTML,但实际上它在底层是 JavaScript!这种语法被称为 JSX,它允许你在 JavaScript 中嵌入标记。

return 语句可以写在一行上,就像这个组件一样

return <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />;

但是,如果你的标记不在与 return 关键字相同的行上,则必须将其用括号括起来

return (
<div>
<img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
</div>
);

陷阱

如果没有括号,return 后面的任何代码都将被 忽略

使用组件

现在你已经定义了 Profile 组件,你可以将其嵌套在其他组件中。例如,你可以导出一个使用多个 Profile 组件的 Gallery 组件

function Profile() {
  return (
    <img
      src="https://i.imgur.com/MK3eW3As.jpg"
      alt="Katherine Johnson"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

浏览器看到的内容

注意大小写差异

  • <section> 是小写,因此 React 知道我们指的是 HTML 标签。
  • <Profile /> 以大写 P 开头,因此 React 知道我们要使用名为 Profile 的组件。

并且 Profile 包含更多 HTML:<img />。最终,浏览器看到的是这些内容

<section>
<h1>Amazing scientists</h1>
<img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
<img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
<img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
</section>

嵌套和组织组件

组件是普通的 JavaScript 函数,因此你可以在同一个文件中保留多个组件。当组件相对较小或彼此紧密相关时,这很方便。如果这个文件变得拥挤,你可以随时将 Profile 移动到单独的文件中。你很快就会在关于导入的 页面 上学习如何做到这一点。

因为 Profile 组件是在 Gallery 内部渲染的——甚至多次!——我们可以说 Gallery 是一个 父组件,将每个 Profile 作为“子组件”渲染。这是 React 的神奇之处:你可以定义一个组件一次,然后在任何地方和任何时候使用它。

陷阱

组件可以渲染其他组件,但是你永远不能嵌套它们的定义:

export default function Gallery() {
// 🔴 Never define a component inside another component!
function Profile() {
// ...
}
// ...
}

上面的代码片段 非常慢并且会导致错误。相反,在顶层定义每个组件

export default function Gallery() {
// ...
}

// ✅ Declare components at the top level
function Profile() {
// ...
}

当子组件需要来自父组件的一些数据时,通过 props 传递它,而不是嵌套定义。

深入探讨

组件一直到底

你的 React 应用程序始于一个“根”组件。通常,在启动新项目时会自动创建它。例如,如果你使用 CodeSandbox 或使用框架 Next.js,根组件在 pages/index.js 中定义。在这些示例中,你一直在导出根组件。

大多数 React 应用程序都使用组件一直到底。这意味着你不仅将组件用于可重用部件(如按钮),还将用于更大的部件(如侧边栏、列表),最终还用于完整的页面!组件是组织 UI 代码和标记的一种方便方法,即使其中一些组件只使用一次。

基于React的框架更进一步。它们不使用空的HTML文件并让React通过JavaScript接管页面管理,而是会自动从你的React组件生成HTML。这允许你的应用程序在JavaScript代码加载之前显示一些内容。

尽管如此,许多网站仅使用React来为现有的HTML页面添加交互性。它们拥有许多根组件,而不是整个页面只有一个根组件。你可以根据需要使用尽可能多或尽可能少的React。

回顾

你刚刚体验了React的入门!让我们回顾一些关键点。

  • React允许你创建组件,应用程序的可重用UI元素。

  • 在React应用程序中,每一块UI都是一个组件。

  • React组件是普通的JavaScript函数,除了

    1. 它们的名称总是以大写字母开头。
    2. 它们返回JSX标记。

挑战 1 4:
导出组件

此沙盒不起作用,因为根组件未导出。

function Profile() {
  return (
    <img
      src="https://i.imgur.com/lICfvbD.jpg"
      alt="Aklilu Lemma"
    />
  );
}

在查看解决方案之前,尝试自行修复它!