使用 React 构建:深入 JSX、组件和 Props! (React Day 2)
Source: Dev.to
(未提供需要翻译的正文内容。如需翻译,请粘贴或提供完整的文本。)
🎉 欢迎回来,React 新手变高手!
在第 1 天的大局概览之后,今天我们深入 让 React 正常运转的核心部分:
- JSX – 让你在 JavaScript 中编写类似 UI 的标记的语法。
- Functional components – 现代 React 的构建块。
- Props – 从父组件流向子组件的只读数据。
- Reusability & composition – 如何将组件拼接在一起,打造强大的应用。
我们将揭开 JSX 的神秘面纱,将其与原生 JavaScript 进行对比,分享最佳实践,提供可在第 3 天的 Vite 环境中运行的实时代码示例,指出常见陷阱,并通过图示可视化关键流程。真实场景将演示每个概念的实际应用。完成后,你将能够像资深开发者一样构建 UI。让我们一起 组件化 吧!
📦 JSX(JavaScript XML)
JSX 是 React 的语法扩展,允许你直接在 JavaScript 中编写类似 HTML 的代码。它并不是真正的 HTML——Babel 会把它转译为
React.createElement调用。
为什么 JSX 很重要
- 将标记和逻辑无缝融合。
- 相比手动操作 DOM,提升可读性和可维护性。
工作原理
- 编写 JSX →
- Babel 将其转译为普通 JavaScript(
React.createElement)。 - React 使用这些元素对象来构建虚拟 DOM。
实时示例
// JSX
const element = <h1>Hello, React!</h1>;
转译后的代码:
// JavaScript after Babel
const element = React.createElement('h1', null, 'Hello, React!');
在组件中渲染
import React from 'react';
function App() {
return <h1>Hello, React!</h1>;
}
将此代码粘贴到
src/App.jsx(或App.tsx)中并运行npm run dev。标题会立即出现。
JSX 与普通 JavaScript 的对比
| JSX | 普通 JavaScript |
|---|---|
{message} | React.createElement('div', null, message) |
| 简洁、类似 HTML | 冗长,难以直观看出嵌套结构 |
结论: JSX 只是一层语法糖;浏览器最终看到的是 JavaScript 版本。
图示 – JSX 转译流程
JSX code
│
▼ (Babel)
React.createElement calls
│
▼ (React runtime)
Virtual DOM → Real DOM
最佳实践
- 组件名称 → 使用 PascalCase(
MyComponent)。 - 自闭合标签 →
<Component />。 - 多行返回 → 用括号包裹:
return (…);。 - 优先使用 CSS 类 而非内联样式对象(仅在必要时使用内联样式)。
常见错误
把 JSX 当作字符串并进行拼接。JSX 生成的是 对象,应通过组件组合 UI,而不是字符串拼接。
实际场景
在一个 博客应用 中,文章预览可以这样渲染:
// Example placeholder – insert actual JSX for a post preview here
JSX 让你可以轻松地将数据(post.title)与结构(<h2>{post.title}</h2>)混合在一起。
⚛️ 函数式组件
函数式组件是普通的 JavaScript(或 TypeScript)函数,返回 JSX。自 React 16.8(Hooks)起,它们成为默认选择,取代了大多数类组件。
为什么使用函数式?
- 语法更简洁,无需
this绑定。 - 与 Hooks 完美配合,用于状态、 side‑effects(副作用)、上下文等。
实时示例
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
function App() {
return <Welcome name="Sara" />;
}
渲染出 “Hello, Sara!”。
结构化建议
- 保持组件小巧 —— 每个组件只负责一个 UI 关注点(单一职责原则)。
- 每个组件一个文件 —— 使用
export default导出组件。 - 为简洁使用 箭头函数:
const Button = () => <button>Click</button>;
- 文件夹结构(可扩展):
src/
└─ components/
└─ Button/
├─ index.jsx
└─ styles.module.css
常见错误
返回多个兄弟元素而没有包装器。使用 Fragments 修复:
return (
<>
<div>One</div>
<div>Two</div>
</>
);
实际场景
一个 ProductCard 函数式组件用于展示商品的图片、名称、价格以及 “加入购物车” 按钮。该组件在商品网格、搜索结果和推荐轮播等多个场景中复用。
Source: …
📤 Props(属性)
Props 是 只读数据,从父组件传递给子组件,类似函数参数。它们使 UI 能够动态、可复用。
Props 的工作原理
// Parent → Child
<Child name="Alice" age={30} />
// Child 接收它们
function Child(props) {
return (
<p>{props.name} is {props.age} years old.</p>
);
}
实时示例
function Greeting(props) {
return (
<p>Hi, {props.name}! You are {props.age} years old.</p>
);
}
function App() {
return (
<>
<Greeting name="Bob" age={25} />
<Greeting name="Carol" age={28} />
</>
);
}
会出现两个个性化的问候。Props 单向流动(父 → 子)。
常见陷阱
| 问题 | 为什么会出问题 | 解决方案 |
|---|---|---|
修改 Props (props.name = 'New') | 破坏不可变性,导致不重新渲染。 | 使用 state 来存放可变数据。 |
| 缺少默认值 | 会得到 undefined。 | 提供 defaultProps 或在解构时设默认值:function Comp({ name = 'Guest' }) { … }。 |
列表中缺少 key | React 无法高效追踪项目。 | 添加唯一的 key 属性:<Item key={id} />。 |
| Prop drilling(层层传递) | 难以维护。 | 使用 Context 或状态管理库。 |
可视化 Props 流向
Parent Component
│ 传递 props
▼
Child Component (接收 props)
│ 可能继续向下传递
▼
Grandchild …
真实场景
在 社交媒体动态 中,Post 组件接收:
<Post author={user} content={text} timestamp={date} />
该组件渲染每条帖子,而不需要硬编码任何数据。
🔁 可重用性
可重用性意味着 只编写一次组件,并在不同的 props 或配置下在各处使用它。
如何实现
- 设计组件以接受 通用 props。
- 保持内部逻辑独立于特定数据。
实时示例 – 可重用按钮
function Button({ label, color, onClick }) {
return (
<button style={{ backgroundColor: color }} onClick={onClick}>
{label}
</button>
);
}
function App() {
return (
<>
<Button label="Submit" color="green" onClick={() => alert('Submitted!')} />
<Button label="Cancel" color="red" onClick={() => alert('Canceled!')} />
</>
);
}
一个组件,两种不同的行为 → DRY 且灵活。
最佳实践
- 使 可选 props 真正可选(提供合理的默认值)。
- 使用 TypeScript 或
prop-types来强制 prop 形状并提前捕获错误。
实际场景
仪表盘中的 Chart 组件接收:
<Chart type="line" data={salesData} options={chartOptions} />
同一个组件即可渲染不同的可视化,而无需重复代码。
🧩 组合
组合是通过 嵌套组件 将简单、可复用的块构建成复杂 UI 的艺术。特殊的 children 属性提供了灵活的“插槽”。
工作原理
- 父组件在 JSX 中包裹子组件。
- 子组件通过
{props.children}渲染它们。
实时示例 – 卡片组合
function Card({ children }) {
return <div className="card">{children}</div>;
}
function App() {
return (
<Card>
<h2>Product Title</h2>
<p>Short description of the product.</p>
<button>Add to cart</button>
</Card>
);
}
Card提供布局;调用方决定内部内容。
好处
- 关注点分离 – 布局 与 内容。
- 可扩展性 – 可以在不修改父组件的情况下替换任何子组件。
✅ TL;DR 检查清单
- ✅ 使用 JSX 编写可读的标记;记住它会编译成
React.createElement。 - ✅ 编写 函数式组件;保持它们小且专注。
- ✅ 从父组件向子组件 只读传递 props;避免对其进行变更。
- ✅ 为 可复用性 设计组件——通用 props,TypeScript/prop‑types。
- ✅ 利用 组合(
children)构建灵活的 UI 结构。
现在就去实现一个带有可复用 Button、Card 包装器以及几个 Greeting 组件的简易 UI——就像上面的示例一样。祝编码愉快! 🚀
组件示例
function Card(props) {
return (
<div className="card">
<h2>{props.title}</h2>
{props.children}
</div>
);
}
function App() {
return (
<Card title="Profile">
<p>Name: Dana</p>
<p>Job: Developer</p>
</Card>
);
}
Card 组件可以组合任意内容。你可以进一步嵌套以实现层级结构。
最佳实践
- 优先使用组合而非继承 – React 鼓励通过组合小的、可重用的组件来构建 UI,而不是依赖基于类的继承。
- 保持嵌套的合理性 – 如果组件树变得过深,考虑重构为更小、更专注的组件。
实际场景
在新闻应用中,Article 组件可能由以下部分组成:
HeaderBodyComments
每个部分都可以在不同的故事中重复使用,而 Article 组件允许自定义内部内容(例如,不同的布局、广告或相关链接)。
回顾与后续步骤
- 你已经掌握了 JSX、函数式组件、props,以及复用/组合的艺术——这是可扩展 React 应用的核心。
- 动手实践: 使用上面的
Card模式构建一个迷你个人资料页。 - 共通主题: 保持 UI 声明式且模块化。
- 遇到错误卡住? 使用 React DevTools 进行调试。
接下来: 第 3 天 – 状态与生命周期!
到目前为止,你最喜欢的示例是哪一个? 继续构建吧! 🚀