React 组件
组件是 React 的核心。它们是独立、可复用的代码片段,各自管理自己的状态和视图。从概念上讲,组件就像是 JavaScript 函数。它们接受任意的输入(我们称之为 "props"),并返回 React 元素,用以描述屏幕上应该显示的内容。
函数组件和 Class 组件
定义组件最简单的方式是编写一个 JavaScript 函数:
// 这是一个函数组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}这个函数是一个有效的 React 组件,因为它接受一个单一的 props (属性) 对象参数,并返回一个 React 元素。
你也可以使用 ES6 的 class 来定义一个组件:
// 这是一个 Class 组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}在现代 React 中,函数组件配合 Hooks 是最常用和推荐的方式。它们更简洁,也更容易理解。我们将在本教程中主要使用函数组件。
渲染组件
之前,我们只遇到过代表 DOM 标签的 React 元素:
const element = <div />;然而,元素也可以代表我们自定义的组件:
const element = <Welcome name="Sara" />;当 React 看到一个代表用户自定义组件的元素时,它会将 JSX 属性作为一个单独的对象传递给这个组件。这个对象被称为 props。
例如,这段代码会在页面上渲染 "Hello, Sara":
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);回顾一下这里发生了什么:
- 我们调用
root.render()并传入<Welcome name="Sara" />元素。 - React 调用
Welcome组件,并将{name: 'Sara'}作为props对象传入。 Welcome组件返回一个<h1>Hello, Sara</h1>元素作为结果。- React DOM 高效地更新 DOM,使其与
<h1>Hello, Sara</h1>匹配。
重要提示: 组件名称必须以大写字母开头。React 会将以小写字母开头的组件视为原生 DOM 标签。例如,
<div />代表一个 HTML div 标签,而<Welcome />则代表一个组件。
组合组件
组件可以在其输出中引用其他组件。这使得我们可以使用同一个组件的抽象来处理任意层次的细节。一个按钮,一个表单,一个对话框,一个屏幕:在 React 应用中,这些通常都被表示为组件。
例如,我们可以创建一个 App 组件,它渲染多个 Welcome 组件:
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 组件:
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:
function Avatar(props) {
return (
<img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} />
);
}然后,我们提取 UserInfo,它在内部渲染 Avatar:
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">{props.user.name}</div>
</div>
);
}现在,我们可以简化 Comment 组件:
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 中有一部分被多次使用(如 Button、Panel),或者它本身就足够复杂(如 App、FeedStory、Comment),那么它就是一个很好的、可以被提取成独立组件的候选项。
接下来,我们将学习组件的一个关键特性:State(状态)。