React 从入门到生产(一):JSX 与组件思维
创作者: Yardon | GitHub: github.com/YardonYan | 版本: v1.0 |
开篇:为什么会有 React
2013 年,Facebook 的工程师们遇到了一个棘手的问题------他们的网站变得越来越复杂,页面上的状态(比如未读消息数、通知小红点、聊天窗口是否打开)像一团乱麻一样纠缠在一起。改一个地方,另一个看似不相关的地方就崩了。
这就是所谓的 "状态同步噩梦"。
想象一下你在餐厅点菜。传统方式(jQuery 时代)就像你跟服务员说了要换一道菜,然后服务员得跑去告诉厨师、改账单、更新库存------而且所有这些都靠服务员手动跑来跑去,漏掉一步就出问题。
React 的方案是:「你只需要告诉我你想要的最终状态,我来负责把页面变成那个样子。」
这句话是理解 React 的核心。你不需要手动操作 DOM、不需要一步步告诉浏览器"先找到这个元素,然后改它的文字,再改它的样式"。你只需要声明:这个组件在某个状态下应该长什么样。
JSX:把 HTML 写进 JavaScript 的魔法
这不是字符串,也不是 HTML
第一次看到 JSX 的人通常会有两个疑问:
"为什么要把 HTML 写到 JavaScript 里,这不是违反了关注点分离原则吗?"
"这语法到底是不是合法的 JavaScript?"
第二个问题好回答:JSX 不是原生 JavaScript 。浏览器不认识 <div>Hello</div> 这种写在 .js 文件里的东西。但是在打包阶段,Babel 或 TypeScript 编译器会把它翻译成标准的 JavaScript 函数调用。
比如你写的:
jsx
const element = <h1 className="greeting">Hello, world!</h1>;
编译后大概变成:
javascript
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
);
所以 JSX 本质上是 React.createElement() 的语法糖。理解这一点很重要------它帮你解释了为什么 JSX 表达式可以赋值给变量、可以在 if 语句里用、可以作为函数的返回值。
JSX 的几个基本规则
规则 1:必须有一个根元素
jsx
// ❌ 不行------两个根元素
return (
<h1>标题</h1>
<p>内容</p>
);
// ✅ 用 Fragment(空标签)包裹
return (
<>
<h1>标题</h1>
<p>内容</p>
</>
);
<>...</> 是一种简写,等价于 <React.Fragment>...</React.Fragment>。它不会在 DOM 里真正创建一个节点。这就像用一根透明的线把两串珍珠穿在一起------线上只有珠子,线本身是看不见的。
规则 2:JavaScript 表达式用花括号
jsx
const name = 'Yardon';
const user = { firstName: 'Yardon', lastName: 'Yan' };
// ✅ 花括号里可以放任何 JavaScript 表达式
<h1>Hello, {name}</h1>
<p>计算结果:{1 + 2 + 3}</p>
<span>{user.firstName} {user.lastName}</span>
// ❌ 但不能放语句
// <div>{if (true) { ... }}</div> // 这是语句,不是表达式
规则 3:className 而非 class,htmlFor 而非 for
jsx
// ❌ class 是 JavaScript 的保留关键字
<div class="container">
// ✅
<div className="container">
<label htmlFor="email">邮箱</label>
规则 4:自闭合标签必须闭合
jsx
// ❌ HTML 里可以偷懒
<img src="avatar.png">
<br>
// ✅ JSX 里必须闭合
<img src="avatar.png" />
<br />
组件的本质:乐高积木思维
React 里最重要的概念就是组件。
组件 = 一个返回 UI 描述的函数
就这么简单。一个 React 组件就是一个 JavaScript 函数,它接收一些参数(props),返回一段 UI 描述(JSX)。
输入(Props)→ 组件函数 → 输出(JSX)
就像乐高积木------每一块积木有自己的形状(UI)和连接点(Props),你可以把许多块积木拼在一起搭出一整座城堡。而且如果某一块坏了,你只需要替换那块积木,不用推倒整座城堡。
来看看一个简单的组件长什么样:
jsx
// UserCard.jsx
function UserCard({ name, role, avatar }) {
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<span className="role-badge">{role}</span>
</div>
);
}
这个组件就像一块蓝色的 2×4 积木------它有自己的外形,有连接点(name, role, avatar),可以嵌入到任何地方。
函数组件 vs 类组件:历史的选择
如果你去看 2018 年之前的 React 教程,你会发现满屏的 class 和 this。
javascript
// 类组件(旧)
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
但从 React 16.8 引入 Hooks 之后,函数组件成为了唯一推荐的方式。原因很简单:
- 代码量少很多------同样的功能,函数组件代码量通常是类组件的 60%
- 没有
this的困惑 ------this在 JavaScript 里是出了名的坑,函数组件里根本不用管它 - 逻辑复用更方便------Hooks 让"状态逻辑"可以抽出来到处用,这是类组件做不到的
2026 年你应该只写函数组件。如果你的项目里还有类组件,那是技术债,该还了。
jsx
// 函数组件(现代)
function Welcome({ name }) {
return <h1>Hello, {name}</h1>;
}
Props:组件之间的对话方式
Props(properties 的缩写)是 React 的神经系统。父组件通过 props 把数据传给子组件,就像你通过电话线把声音传到另一端。
Props 是只读的
这是 React 的铁律:组件绝不能修改自己的 props。
jsx
function Greeting({ name }) {
// ❌ 绝对不要这样做!
// name = '修改后的名字';
return <h1>Hello, {name}</h1>;
}
为什么?因为 React 的渲染模型建立在纯函数的概念之上。同样的输入(props),必须产生同样的输出(UI)。如果组件可以修改 props,整个「声明式」范式就崩塌了------你会回到手动追踪状态变化的地狱。
可以把 props 想象成你在餐厅点的菜单。你告诉服务员(父组件)你要什么,厨房(子组件)按照你的菜单做菜,但厨房不会修改你的菜单内容。
默认 Props
jsx
function Button({ text = '点击', size = 'md', disabled = false }) {
return (
<button
className={`btn btn-${size}`}
disabled={disabled}
>
{text}
</button>
);
}
// 不传 text 时默认显示「点击」
<Button size="lg" />
// 传了就覆盖
<Button text="提交表单" size="sm" />
组件拆分原则:什么时候该拆
初学者常犯两个错误:要么一个组件写成 500 行的「巨无霸」,要么把每个 <div> 都拆成独立组件。
判断标准其实很简单,问自己三个问题:
问题 1:这部分会重复出现吗?
如果你发现自己在复制粘贴同一段 JSX,那它就应该被抽成一个组件。比如博客文章卡片、用户头像、评分星星。
jsx
// 出现三次了------该拆!
{users.map(user => (
<div className="user-card">
<img src={user.avatar} />
<span>{user.name}</span>
</div>
))}
// 拆出来:
<UserCard user={user} />
问题 2:这部分有独立的逻辑吗?
如果一段代码有自己的状态(useState)、自己的副作用(useEffect)、自己的事件处理------它很可能值得独立成一个组件。
问题 3:你看这个组件时,需要滚动屏幕吗?
一个朴素的指标:如果组件代码超过 150 行,99% 的情况下它应该被拆分。这不是教条,而是------当一个组件长到你需要在编辑器里上下翻页时,理解它的心智负担已经超标了。
拆分原则速查表:
┌────────────────┬──────────────────┐
│ 条件 │ 行动 │
├────────────────┼──────────────────┤
│ 相同 JSX 出现 3+ 次 │ 抽成独立组件 │
│ 有自己的 state │ 抽成独立组件 │
│ 超过 150 行 │ 拆分为 2-3 个子组件 │
│ 逻辑可以被复用 │ 抽成独立组件 │
│ 只有一个 <div> │ 不用拆,保持简单 │
└────────────────┴──────────────────┘
实战:搭建第一个 React 应用
用 Vite 创建一个 React 项目(2026 年,Vite 已经是前端事实标准):
bash
# 创建项目
npm create vite@latest my-first-react -- --template react
# 进入目录
cd my-first-react
# 安装依赖
npm install
# 启动开发服务器
npm run dev
打开 src/App.jsx,把它替换为你的第一个组件:
jsx
function App() {
const skills = ['React', 'TypeScript', 'Node.js', 'Python'];
return (
<div className="app">
<header>
<h1>👋 你好,我是 Yardon</h1>
<p>一个正在学习 React 的开发者</p>
</header>
<main>
<h2>我的技术栈</h2>
<ul>
{skills.map(skill => (
<li key={skill}>{skill}</li>
))}
</ul>
</main>
<footer>
<p>© 2026 Yardon. All rights reserved.</p>
</footer>
</div>
);
}
export default App;
刷新浏览器,你应该看到一个简洁的个人页。是的,React 就是这么开始的------从最简单的组件起步,一步步搭建起复杂的应用。
本章小结
| 概念 | 一句话总结 |
|---|---|
| JSX | JavaScript 的语法扩展,让你用类似 HTML 的语法描述 UI,编译为 createElement() |
| 组件 | 返回 UI 描述的函数,像乐高积木一样可组合 |
| 函数组件 | 2026 年唯一推荐的组件写法,Hooks 赋予了完整能力 |
| Props | 父组件传递给子组件的只读数据,是组件间的通信通道 |
| 拆分 | 超过 150 行或重复出现 3 次就拆,保持每个组件的职责单一 |
下一章,我们将深入 状态与事件处理 ------真正让组件"活起来"的秘密。你会发现,掌握了 useState 就掌握了 React 一半的心智模型。
📌 创作者: Yardon | 🏠 个人网站: GlimmerAI.top
📖 本章是「React 从入门到生产」系列的第 1 章。本系列共 8 章,带你从零开始掌握 React 开发。
🌟 如果你觉得有帮助,欢迎访问 GlimmerAI.top 查看我的更多作品。欢迎大家来观看!