您的第一个组件

组件是 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"
    />
  );
}

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