你的第一个组件

组件是 React 的核心概念之一。它们是构建用户界面(UI)的基础,因此是开始学习 React 的理想起点!

你将学习到

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

组件:用户界面构建块

🌐 Components: UI building blocks

在网络上,HTML 让我们能够使用其内置的一套标签(如 <h1><li>)创建丰富的结构化文档:

🌐 On the Web, HTML lets us create rich structured documents with its built-in set of tags like <h1> and <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,构成了每个侧边栏、头像、模态窗口、下拉菜单——你在网页上看到的每一块用户界面背后的基础。

🌐 This markup represents this article <article>, its heading <h1>, and an (abbreviated) table of contents as an ordered list <ol>. Markup like this, combined with CSS for style, and JavaScript for interactivity, lies behind every sidebar, avatar, modal, dropdown—every piece of UI you see on the Web.

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

🌐 React lets you combine your markup, CSS, and JavaScript into custom “components”, reusable UI elements for your app. The table of contents code you saw above could be turned into a <TableOfContents /> component you could render on every page. Under the hood, it still uses the same HTML tags like <article>, <h1>, etc.

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

🌐 Just like with HTML tags, you can compose, order and nest components to design whole pages. For example, the documentation page you’re reading is made out of React components:

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

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

🌐 As your project grows, you will notice that many of your designs can be composed by reusing components you already wrote, speeding up your development. Our table of contents above could be added to any screen with <TableOfContents />! You can even jumpstart your project with the thousands of components shared by the React open source community like Chakra UI and Material UI.

定义组件

🌐 Defining a component

传统上,在创建网页时,网页开发者会先为他们的内容进行标记,然后通过加入一些 JavaScript 来增加交互功能。当交互在网页上只是锦上添花时,这种方法效果很好。现在,对于许多网站和所有应用来说,交互是必须的。React 把交互性放在首位,同时仍然使用相同的技术:React 组件是一个 JavaScript 函数,你可以在其中加入标记。 下面是示例(你可以编辑下面的例子):

🌐 Traditionally when creating web pages, web developers marked up their content and then added interaction by sprinkling on some JavaScript. This worked great when interaction was a nice-to-have on the web. Now it is expected for many sites and all apps. React puts interactivity first while still using the same technology: a React component is a JavaScript function that you can sprinkle with markup. Here’s what that looks like (you can edit the example below):

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

以下是构建组件的方法:

🌐 And here’s how to build a component:

步骤 1:导出组件

🌐 Step 1: Export the component

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

🌐 The export default prefix is a standard JavaScript syntax (not specific to React). It lets you mark the main function in a file so that you can later import it from other files. (More on importing in Importing and Exporting Components!)

步骤 2:定义函数

🌐 Step 2: Define the function

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

🌐 With function Profile() { } you define a JavaScript function with the name Profile.

易犯错误

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

🌐 React components are regular JavaScript functions, but their names must start with a capital letter or they won’t work!

步骤 3:添加标记

🌐 Step 3: Add markup

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

🌐 The component returns an <img /> tag with src and alt attributes. <img /> is written like HTML, but it is actually JavaScript under the hood! This syntax is called JSX, and it lets you embed markup inside JavaScript.

返回语句可以全部写在一行中,就像在这个组件中一样:

🌐 Return statements can be written all on one line, as in this component:

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

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

🌐 But if your markup isn’t all on the same line as the return keyword, you must wrap it in a pair of parentheses:

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

易犯错误

没有括号的话,return 之后的任何代码 将被忽略

🌐 Without parentheses, any code on the lines after return will be ignored!

使用组件

🌐 Using a component

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

🌐 Now that you’ve defined your Profile component, you can nest it inside other components. For example, you can export a Gallery component that uses multiple Profile components:

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>
  );
}

浏览器看到的

🌐 What the browser sees

注意外壳的区别:

🌐 Notice the difference in casing:

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

Profile 包含更多的 HTML:<img />。最终,浏览器看到的是:

🌐 And Profile contains even more HTML: <img />. In the end, this is what the browser sees:

<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>

嵌套和组织组件

🌐 Nesting and organizing components

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

🌐 Components are regular JavaScript functions, so you can keep multiple components in the same file. This is convenient when components are relatively small or tightly related to each other. If this file gets crowded, you can always move Profile to a separate file. You will learn how to do this shortly on the page about imports.

因为 Profile 组件被渲染在 Gallery 内——甚至渲染了好几次!——我们可以说 Gallery 是一个**父组件,**将每个 Profile 渲染为“子组件”。这是 React 的魔力之一:你可以定义一个组件一次,然后在任意地方、多次使用它。

🌐 Because the Profile components are rendered inside Gallery—even several times!—we can say that Gallery is a parent component, rendering each Profile as a “child”. This is part of the magic of React: you can define a component once, and then use it in as many places and as many times as you like.

易犯错误

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

🌐 Components can render other components, but you must never nest their definitions:

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

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

🌐 The snippet above is very slow and causes bugs. Instead, define every component at the top level:

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

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

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

🌐 When a child component needs some data from a parent, pass it by props instead of nesting definitions.

深入研究

组件一直向下

🌐 Components all the way down

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

🌐 Your React application begins at a “root” component. Usually, it is created automatically when you start a new project. For example, if you use CodeSandbox or if you use the framework Next.js, the root component is defined in pages/index.js. In these examples, you’ve been exporting root components.

大多数 React 应用在各个层面上都使用组件。这意味着你不仅会将组件用于可重用的部分,比如按钮,还会用于更大的部分,比如侧边栏、列表,最终甚至是完整的页面!组件是一种组织 UI 代码和标记的方便方式,即使其中有些组件只使用一次。

🌐 Most React apps use components all the way down. This means that you won’t only use components for reusable pieces like buttons, but also for larger pieces like sidebars, lists, and ultimately, complete pages! Components are a handy way to organize UI code and markup, even if some of them are only used once.

基于 React 的框架 更进一步。它们不仅使用一个空的 HTML 文件并让 React “接管”用 JavaScript 管理页面,它们从你的 React 组件自动生成 HTML。这使你的应用在 JavaScript 代码加载之前就能显示一些内容。

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

🌐 Still, many websites only use React to add interactivity to existing HTML pages. They have many root components instead of a single one for the entire page. You can use as much—or as little—React as you need.

回顾

你刚刚体验了 React 的初步使用!让我们回顾一些关键点。

🌐 You’ve just gotten your first taste of React! Let’s recap some key points.

  • React 让你可以创建组件,可在你的应用中重复使用的用户界面元素。
  • 在 React 应用中,每一块 UI 都是一个组件。
  • React 组件是常规的 JavaScript 函数,除了:
    1. 它们的名称总是以大写字母开头。
    2. 它们返回 JSX 标记。

挑战 1 of 4:
导出组件

🌐 Export the component

此沙箱不起作用,因为未导出根组件:

🌐 This sandbox doesn’t work because the root component is not exported:

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

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

🌐 Try to fix it yourself before looking at the solution!