Skip to content

React 组件

组件是 React 的核心。它们是独立、可复用的代码片段,各自管理自己的状态和视图。从概念上讲,组件就像是 JavaScript 函数。它们接受任意的输入(我们称之为 "props"),并返回 React 元素,用以描述屏幕上应该显示的内容。

函数组件和 Class 组件

定义组件最简单的方式是编写一个 JavaScript 函数:

javascript
// 这是一个函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

这个函数是一个有效的 React 组件,因为它接受一个单一的 props (属性) 对象参数,并返回一个 React 元素。

你也可以使用 ES6 的 class 来定义一个组件:

javascript
// 这是一个 Class 组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

在现代 React 中,函数组件配合 Hooks 是最常用和推荐的方式。它们更简洁,也更容易理解。我们将在本教程中主要使用函数组件。

渲染组件

之前,我们只遇到过代表 DOM 标签的 React 元素:

javascript
const element = <div />;

然而,元素也可以代表我们自定义的组件:

javascript
const element = <Welcome name="Sara" />;

当 React 看到一个代表用户自定义组件的元素时,它会将 JSX 属性作为一个单独的对象传递给这个组件。这个对象被称为 props

例如,这段代码会在页面上渲染 "Hello, Sara":

javascript
import React from 'react';
import ReactDOM from 'react-dom/client';

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;
root.render(element);

回顾一下这里发生了什么:

  1. 我们调用 root.render() 并传入 <Welcome name="Sara" /> 元素。
  2. React 调用 Welcome 组件,并将 {name: 'Sara'} 作为 props 对象传入。
  3. Welcome 组件返回一个 <h1>Hello, Sara</h1> 元素作为结果。
  4. React DOM 高效地更新 DOM,使其与 <h1>Hello, Sara</h1> 匹配。

重要提示: 组件名称必须以大写字母开头。React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div /> 代表一个 HTML div 标签,而 <Welcome /> 则代表一个组件。

组合组件

组件可以在其输出中引用其他组件。这使得我们可以使用同一个组件的抽象来处理任意层次的细节。一个按钮,一个表单,一个对话框,一个屏幕:在 React 应用中,这些通常都被表示为组件。

例如,我们可以创建一个 App 组件,它渲染多个 Welcome 组件:

javascript
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

通常,新的 React 应用会有一个顶层的 App 组件,它包含了整个应用。

提取组件

不要害怕将组件拆分成更小的组件。

思考一下这个 Comment 组件:

javascript
function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} />
        <div className="UserInfo-name">{props.author.name}</div>
      </div>
      <div className="Comment-text">{props.text}</div>
      <div className="Comment-date">{formatDate(props.date)}</div>
    </div>
  );
}

这个组件因为包含了太多嵌套的结构而难以修改。我们可以从中提取出一些小组件。

首先,我们提取 Avatar

javascript
function Avatar(props) {
  return (
    <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} />
  );
}

然后,我们提取 UserInfo,它在内部渲染 Avatar

javascript
function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">{props.user.name}</div>
    </div>
  );
}

现在,我们可以简化 Comment 组件:

javascript
function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">{props.text}</div>
      <div className="Comment-date">{formatDate(props.date)}</div>
    </div>
  );
}

组件的提取一开始可能看起来像是繁重的工作,但它在大型应用中能带来巨大的回报。一个好的经验法则是,如果你的 UI 中有一部分被多次使用(如 ButtonPanel),或者它本身就足够复杂(如 AppFeedStoryComment),那么它就是一个很好的、可以被提取成独立组件的候选项。

接下来,我们将学习组件的一个关键特性:State(状态)