3. 组件基础
这是什么:React 组件 = 一个返回 JSX 的函数,没有模板、没有多文件格式,一个函数就是一个组件。
为什么用:上一章把整页写在一个组件里,再多几块就乱了。把页面拆成小函数组件,各块只管自己那块,复用和分工都变简单。
如何用:本章先写一个 TaskItem 组件,再用它在父组件里循环渲染,最后用 children 学会「往标签中间装内容」。
3.1 函数组件:一整页就是几个函数
任务条目拆出来,页面立刻清爽——整段可跑:
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<script src='https://registry.npmmirror.com/react/18.3.1/files/umd/react.production.min.js'></script>
<script src='https://registry.npmmirror.com/react-dom/18.3.1/files/umd/react-dom.production.min.js'></script>
<script src='https://registry.npmmirror.com/@babel/standalone/7.29.11/files/babel.min.js'></script>
</head>
<body>
<div id='root'></div>
<script type='text/babel' data-presets='react'>
function TaskItem({ text }) { // 组件名必须大写开头:小写会被当 HTML 标签
return <li>{text}</li>
}
function App() { // 函数组合:App 里装小型组件
return (
<ul>
<TaskItem text='学 React' />
<TaskItem text='写教程' />
</ul>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
</script>
</body>
</html>
你会看到:两条任务「学 React」「写教程」——<TaskItem /> 用起来像 HTML 标签,其实是函数。
要点:
- 组件名必须大写开头(
<TaskItem>是组件、<taskitem>会被当 HTML 标签) - 参数解构
{ text }接外部传入数据(就是 props,下一章细讲) - 免构建阶段一个
<text/babel>块可以放多个组件;工程化阶段是一个文件一个组件、文件名与组件名相同
3.2 组件即函数:真正的复用 + children 通用外皮
Welcome 复用与 Card 组合放进一页看个痛快:
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<script src='https://registry.npmmirror.com/react/18.3.1/files/umd/react.production.min.js'></script>
<script src='https://registry.npmmirror.com/react-dom/18.3.1/files/umd/react-dom.production.min.js'></script>
<script src='https://registry.npmmirror.com/@babel/standalone/7.29.11/files/babel.min.js'></script>
</head>
<body>
<div id='root'></div>
<script type='text/babel' data-presets='react'>
function Welcome({ name }) {
return <h2>欢迎,{name}!</h2>
}
function Card({ title, children }) {
return (
<div style={{ border: '1px solid #ddd', padding: '12px', marginTop: '12px' }}>
<h3>{title}</h3>
{children} {/* 承接标签中间的内容(Vue 的 slot) */}
</div>
)
}
function App() {
return (
<div>
<Welcome name='青蛙' />
<Welcome name='熊猫' />
<Card title='公告'>
<p>这里放任意 JSX</p>
<p>第二行也照样收</p>
</Card>
</div>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
</script>
</body>
</html>
你会看到:两行「欢迎,青蛙!」「欢迎,熊猫!」——组件 = 带 UI 输出的函数,传什么 props 就画什么画面;下面还有一张带「公告」标题的卡片,里面的内容就是 children(Vue 里的 <slot>)。
3.3 页面组织
免构建阶段,所有组件都在同一个 <text/babel> 块里,位置毫无讲究;但工程化(Vite)阶段的目录习惯值得先见一眼:
src/
├── App.jsx # 根组件
├── components/ # 一般组件
│ ├── TaskItem.jsx
│ └── TaskList.jsx
└── main.jsx # 启动器
每章页面末尾那行就是启动器本体:ReactDOM.createRoot(document.getElementById('root')).render(<App />)——Vue 的「index.html 壳 → main 启动 → App 根 → 子树」React 一模一样,只是启动函数叫 createRoot。
3.4 组合:一切靠组件树
组件可以无限 nesting:Card 的 children 里还能再装 Card 或 TaskItem——页面上每个可见块都是树上一个组件节点。动手改一节里你会亲手做出三层树。
3.5 动手改
- 在 3.1 页面写
Badge组件,传入text显示彩色角标 - 用
<Card title='技能'>包住 Badge × 3,复用卡片 - 给 Card 加一个可选 props
footer(不传时{footer}就是 undefined,验证 JSX 不渲染它)
3.6 小结
- 组件 = 返回 JSX 的大写函数,props 当参数
children承接嵌套内容,复用与组合都靠它- 启动三行:开启容器 + createRoot + render;页面组织免构建阶段全在一块
下一章:state 与事件处理 🐸