React 函数组件
掌握了 JSX,我们来看它的"宿主"——组件。在 React 里,组件是代码复用和组织的核心单位。好消息是:React 的组件定义极其简单,简单到一句话能说完——"一个返回 JSX 的函数就是一个组件"。
1. 定义第一个组件
组件就是一个普通的 JS 函数,满足两个条件:(1) 函数名首字母大写;(2) 返回一段 JSX。
// 一个最简单的组件:就是一个返回 JSX 的函数
function Welcome() {
return <h1>你好,欢迎学习 React!</h1>;
}
// 用法和 HTML 标签一样,像写自定义标签
function App() {
return (
<div>
<Welcome />
<Welcome />
</div>
);
}这就是一个完整的 React 组件了。在别的组件里,你可以像写 HTML 标签一样使用它:<Welcome />。React 看到这个标签,就会去调用 Welcome 函数,把它返回的 JSX 渲染到那个位置。
2. 命名规则:首字母必须大写
这是 React 的硬性约定,违反会出 bug:
// ✅ 正确:组件名首字母大写(PascalCase)
function UserCard() { ... }
function ShoppingCart() { ... }
// ❌ 错误:首字母小写,React 会当成原生 HTML 标签
function userCard() { ... } // 不会被识别为组件
// 原因:React 看到大写字母开头的标签
// → 当作"自定义组件"渲染
// 看到小写字母开头
// → 当成原生 HTML(div/span)渲染这个规则的根本原因,是 React 需要一种方式区分"原生 HTML 标签"(div、span、h1)和"自定义组件"(Welcome、UserCard)。它选了最简单的:首字母大小写。记住这条,你就再也不会写出"组件不渲染"的 bug。
3. 组件组合:搭积木
React 的精髓是组合(composition):小组件拼成大组件,大组件拼成页面。整个 React 应用,本质上就是一棵"组件树"。
// 定义几个小组件
function Avatar() {
return <img src="/me.jpg" className="avatar" alt="头像" />;
}
function UserName() {
return <span className="name">小明</span>;
}
// 把它们组合成一个更大的组件
function UserCard() {
return (
<div className="card">
<Avatar />
<UserName />
</div>
);
}
// 再组合进整个页面
function App() {
return (
<main>
<UserCard />
<UserCard />
<UserCard />
</main>
);
}这种写法的好处显而易见:每个组件只管自己那一小块,改头像不影响评论,改评论不影响购物车。高内聚、低耦合——软件工程的黄金法则,React 天然支持。
4. 抽取组件:把大块拆成小块
新手常犯的错误是:把所有东西塞进一个巨大的 App 组件,几百行 JSX,改起来痛苦。正确的做法是主动抽取——看到一个独立的视觉块(用户卡片、评论、按钮组),就把它拆成独立组件。
// ❌ 反例:一个"巨型组件"什么都干,难维护
function Comment() {
return (
<div className="comment">
<div className="user-info">
<img src="/me.jpg" alt="头像" />
<div>小明</div>
</div>
<div className="text">这篇文章真好</div>
<div className="date">2024-01-01</div>
</div>
);
}
// ✅ 重构:抽出独立组件,职责单一
function UserInfo({ name, avatar }) {
return (
<div className="user-info">
<img src={avatar} alt="头像" />
<div>{name}</div>
</div>
);
}
function Comment({ name, avatar, text, date }) {
return (
<div className="comment">
<UserInfo name={name} avatar={avatar} />
<div className="text">{text}</div>
<div className="date">{date}</div>
</div>
);
}判断什么时候该抽组件,有个简单的经验法则:如果一个 JSX 块在别处还会复用,或者它有自己独立的"职责"(如展示用户信息),就抽出来。抽出来的组件命名要有意义(UserInfo 而不是 Box1),让人看名字就懂它干什么。
5. 组件是纯函数(重要理念)
React 期望你的函数组件是纯函数:相同的输入(props)永远返回相同的输出,且不产生副作用(不修改外部变量、不发请求、不改 DOM)。
// 纯函数组件:同样的输入(props)→ 同样的输出(JSX)
function Greeting({ name }) {
return <h1>你好,{name}!</h1>;
}
// 调用 100 次结果都一样
// → React 能安全地复用、跳过渲染,性能更好
// ❌ 反例:在组件里修改外部变量(不纯)
let clickCount = 0;
function BadButton() {
clickCount++; // 副作用!不要在渲染时做
return <button>点击 {clickCount}</button>;
}为什么这么严格?因为 React 可能随时多次调用你的组件(开发模式的 StrictMode 故意调两次),如果组件里有副作用,就会出各种诡异的 bug。需要"副作用"(发请求、改状态、操作 DOM)的场合,React 提供了专门的工具(useState、useEffect),后面会讲。
6. 不要在组件里定义组件
新手另一个常见错误:把组件定义在另一个组件内部。看起来"内聚",其实是大坑:
- 每次外层组件渲染,内层组件都会被重新创建成一个新的函数,React 会认为它"变了",整个子树卸载重建,状态全丢。
- 正确做法:把内层组件提到外面定义,或抽成独立文件。
7. 一个文件一个组件
社区约定:一个 .jsx 文件只导出一个组件(root 组件 App 例外)。文件名和组件名一致(UserCard.jsx 里导出 UserCard),这样别人 import 时一眼就知道里面是什么。组件多了就建文件夹分类:
src/components/放通用组件src/pages/放页面级组件src/hooks/放自定义 Hook
小结
组件 = 返回 JSX 的函数,首字母大写。React 应用是组件组合的树。好的组件是纯函数 + 单一职责 + 可复用。下一篇我们看组件之间如何"传数据"——这就是 Props。
← 上一篇 JSX 语法
下一篇 Props 属性 →