一、什么是 React 组件?
在 React 中,组件是 UI 的基本构建单元。你可以把它想象成"乐高积木"------每个组件负责一小块功能(如头部、列表、按钮),通过组合这些组件,最终搭建出完整的页面。
✅ 组件的本质
- 函数组件(Function Component):一个返回 JSX 的 JavaScript 函数。
- 类组件(Class Component):ES6 类(已逐渐被函数组件 + Hooks 取代)。
- 本文聚焦于函数组件,这是现代 React 开发的主流方式。
javascript
jsx
编辑
// 示例:一个简单的函数组件
function Greeting() {
return <h1>Hello, React!</h1>;
}
💡 关键点:只要一个函数返回 JSX,它就是一个 React 组件。
二、JSX:JavaScript + XML 的语法糖
1. JSX 是什么?
JSX(JavaScript XML)是 React 官方推荐的模板语法,允许你在 JavaScript 中直接编写类似 HTML 的结构。
ini
jsx
编辑
const element = <h2>JSX 是 React 中用于描述用户界面的语法扩展</h2>;
这行代码看起来像 HTML,但实际上是 JavaScript 的语法扩展。Babel 会将其编译为 React.createElement() 调用:
ini
js
编辑
const element2 = React.createElement('h2', null, 'JSX 是 React 中用于描述用户界面的语法扩展');
两者完全等价!JSX 只是更简洁、可读性更强的写法。
2. JSX 语法规则速查
| 场景 | HTML 写法 | JSX 写法 | 说明 |
|---|---|---|---|
| 属性名 | class |
className |
class 是 JS 保留字 |
| 事件绑定 | onclick |
onClick |
驼峰命名 |
| 内联样式 | style="color:red" |
style={{ color: 'red' }} |
对象形式,值为字符串或数字 |
| 注释 | <!-- 注释 --> |
{/* 注释 */} |
必须在花括号内 |
| 条件渲染 | --- | {isLoggedIn ? <div>已登录</div> : <div>未登录</div>} |
使用三元或逻辑运算符 |
| 列表渲染 | --- | {list.map(item => <li key={item.id}>{item.text}</li>)} |
必须加 key |
⚠️ 注意:
- JSX 标签必须闭合(如
<img />)。- 最外层只能有一个根元素(可用
<>...</>片段解决)。
三、状态管理:useState Hook
React 组件需要响应用户交互或数据变化,这就需要状态(State) 。
示例:使用 useState
javascript
jsx
编辑
import { useState } from 'react';
function App() {
const [name, setName] = useState('Vue'); // 初始值为 'Vue'
setTimeout(() => {
setName('React'); // 3 秒后更新为 'React'
}, 3000);
return <h1>Hello {name}!</h1>;
}
useState返回一个数组:[当前状态值, 更新函数]。- 调用
setName()会触发组件重新渲染。
🔁 对比 Vue :
useState类似于 Vue 3 的ref(),但 React 的状态更新是不可变的(需返回新值)。
四、「取反操作」:布尔状态切换的最佳实践
你可能已经注意到如下代码:
scss
jsx
编辑
const [isLoggedIn, setIsLoggedIn] = useState(false);
const toggleLogin = () => {
setIsLoggedIn(!isLoggedIn); // 关键:对状态取反
};
这个看似简单的 !isLoggedIn,其实体现了 React 开发中处理布尔型状态切换(如登录/退出、显示/隐藏、开关)的经典设计模式。下面我们深入拆解它的原理和价值。
一、先明确核心变量的含义
scss
jsx
编辑
// 初始化 isLoggedIn 为 false → 表示「未登录」
const [isLoggedIn, setIsLoggedIn] = useState(false);
-
isLoggedIn是登录状态的唯一数据源:false= 未登录;true= 已登录。
-
所有与登录相关的 UI(按钮文本、"已/未登入"提示)都直接依赖这个变量渲染。
-
这正是 React 「数据驱动视图」 的核心思想:状态变了,UI 自动更新。
二、为什么要做「取反操作」?
1. 本质:一个函数处理两种互斥行为,避免冗余
如果不使用取反,你需要分别定义两个函数:
ini
jsx
编辑
// ❌ 冗余写法(不推荐)
const login = () => setIsLoggedIn(true);
const logout = () => setIsLoggedIn(false);
<button onClick={isLoggedIn ? logout : login}>
{isLoggedIn ? '退出登入' : '登入'}
</button>
而使用取反,只需一个函数:
javascript
jsx
编辑
// ✅ 简洁写法
const toggleLogin = () => setIsLoggedIn(!isLoggedIn);
<button onClick={toggleLogin}>
{isLoggedIn ? '退出登入' : '登入'}
</button>
逻辑映射清晰:
- 当前
isLoggedIn = false(未登录)→!isLoggedIn = true→ 执行登录; - 当前
isLoggedIn = true(已登录)→!isLoggedIn = false→ 执行退出。
2. 逻辑闭环:状态与 UI 完全联动
React 的优势在于状态驱动 UI 自动更新。取反操作让整个交互形成闭环:
| 操作 | isLoggedIn 变化 |
UI 自动更新结果 |
|---|---|---|
| 初始 | false |
显示「未登入」,按钮为「登入」 |
| 点击一次 | false → true |
显示「已登入」,按钮为「退出登入」 |
| 再点一次 | true → false |
显示「未登入」,按钮为「登入」 |
你不需要手动操作 DOM 修改文本或样式------一切由状态驱动,自动完成。
🆚 对比原生 JS :传统开发需
document.getElementById().innerText = ...,容易出错且难以维护。
3. 扩展性强:后续逻辑集中一处,易于维护
假设未来要加入真实登录/登出逻辑(如调接口、存 Token),只需在 toggleLogin 中扩展:
javascript
jsx
编辑
const toggleLogin = async () => {
const newState = !isLoggedIn;
setIsLoggedIn(newState); // 先更新 UI(提升体验)
if (newState) {
// 登录:调接口 + 存 Token
await axios.post('/api/login');
localStorage.setItem('token', 'xxx');
alert('登录成功!');
} else {
// 退出:调接口 + 清 Token
await axios.post('/api/logout');
localStorage.removeItem('token');
alert('已退出登录!');
}
};
如果当初写了 login 和 logout 两个函数,这里就要同时修改两处 ,容易遗漏。而「切换函数」只需改一处,逻辑高度集中。
五、组件化实战:构建掘金首页布局
下面是一个典型的组件树结构,模拟掘金首页:
javascript
jsx
编辑
function App() {
return (
<div>
<JuejinHeader />
<main>
<Articles />
<aside>
<Checkin />
<TopArticles />
</aside>
</main>
</div>
);
}
// 子组件定义
const JuejinHeader = () => <header><h1>掘金首页</h1></header>;
const Articles = () => <div>文章列表</div>;
const Checkin = () => <div>签到区</div>;
const TopArticles = () => <div>热门文章</div>;
🌲 组件树 vs DOM 树
- 传统开发 :直接操作 DOM 节点(如
document.getElementById)。 - React 开发 :操作组件树,由 React 自动同步到真实 DOM(虚拟 DOM 机制)。
✅ 优势:逻辑解耦、复用性强、易于测试和维护。
六、条件渲染与列表渲染
1. 条件渲染
css
jsx
编辑
{isLoggedIn ? <div>已登录</div> : <div>未登录</div>}
<button onClick={toggleLogin}>
{isLoggedIn ? '退出登录' : '登录'}
</button>
2. 列表渲染(带 key)
javascript
jsx
编辑
{todos.length > 0 ? (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
) : (
<div>没有待办事项</div>
)}
🔑 为什么需要 key?
key帮助 React 识别哪些元素被添加、删除或移动,提升渲染性能。不要用 index 作为 key(除非列表静态不变) 。
七、总结要点
| 主题 | 核心要点 |
|---|---|
| 组件 | 函数返回 JSX 即组件;是 React 开发的基本单位 |
| JSX | 语法糖,编译为 React.createElement;支持嵌入 JS 表达式 |
| 状态 | useState 管理局部状态;更新状态触发重渲染 |
| 取反操作 | 用 !state 实现布尔状态切换,一个函数替代两个,逻辑统一、可扩展 |
| 组件化 | 自上而下组合组件,形成组件树;替代传统 DOM 操作 |
| 渲染控制 | 条件渲染用三元/&&;列表渲染必须带唯一 key |
八、拓展思考
Q1:JSX 是必须的吗?
不是 。你可以全程使用 React.createElement,但 JSX 极大提升了可读性和开发效率,官方强烈推荐。
Q2:React 与 Vue 的组件思想有何异同?
| 维度 | React | Vue |
|---|---|---|
| 模板 | JSX(JS 内写 HTML) | SFC(<template> + <script> + <style>) |
| 状态 | useState / useReducer |
ref / reactive |
| 更新机制 | 不可变数据 + 重新渲染 | 响应式代理(自动追踪依赖) |
| 学习曲线 | 较陡(需理解函数式、闭包、Hooks) | 较平缓(选项式 API 直观) |
🎯 建议:若你熟悉函数式编程,React 更灵活;若偏好模板分离,Vue 更直观。
九、注意事项(避坑指南)
- 不要直接修改 state
❌todos[0].done = true;
✅ 使用展开运算符或不可变更新方式。 - 事件处理函数使用箭头函数或正确绑定 this
- 避免在 render 中定义函数或对象(会导致子组件不必要重渲染)
- key 要稳定、唯一、可预测,优先使用 ID 而非数组索引
- 布尔状态切换优先使用「取反」模式
这是处理登录/退出、开关、显隐等场景的最佳实践,简洁、健壮、易扩展。