🧩 先搞懂:React 是什么?
React 是 Meta(原 Facebook)开源的前端 UI 框架,让你能用“组件”的方式构建用户界面。你可以把它理解为乐高积木——每个积木就是一个组件,拼在一起就是你的网页。
⚡ 核心结论一句话
React 的核心思想是:UI = f(state),即页面是数据的函数视图,数据变了,页面自动跟着变。
📋 支持的核心理念一览
| 概念 | 作用 | 类比 |
|---|---|---|
| Component | 可复用的 UI 单元 | 乐高积木块 🔥主力 |
| JSX | 在 JS 里写类似 HTML 的标签 | 混合写法 |
| Props | 父传给子的参数 | 函数参数 |
| State | 组件自己的记忆 | 变量但能触发重渲染 |
| Hook | 钩子函数,“接入” React 特性 | 插件接口 |
一、你的第一个组件
最简单的组件写法
// 快捷方式:直接传字符串,快速实验用Stringreply=chatModel.chat("你好");换成 React 就是:
function MyButton() { return <button>我是一个按钮</button>; }这个函数返回一段 JSX(看起来像 HTML 的东西),React 就会把它渲染到页面上。
标准用法:嵌套组件
export default function MyApp() { return ( <div> <h1>欢迎来到我的应用</h1> <MyButton /> {/* 注意大写!这是自定义组件 */} </div> ); }关键规则:
- 自定义组件名必须以大写字母开头(
MyButton),HTML 原生标签用小写(<div>) export default表示这是文件的主组件
二、JSX 的规则(3 条铁律)
JSX 长得像 HTML,但有更严格的规则:
1. 只能返回一个根元素
// ❌ 错误:多个顶级标签 return ( <h1>Hello</h1> <p>World</p> ); // ✅ 正确:用一个 div 包裹 return ( <div> <h1>Hello</h1> <p>World</p> </div> ); // ✅ 或者用 Fragment <>...</>(不产生额外 DOM 节点) return ( <> <h1>Hello</h1> <p>World</p> </> );2. 标签必须闭合
// ❌ HTML 中可以省略闭合 <img src="cat.jpg" class="avatar"> // ✅ JSX 必须自闭合 <img src="cat.jpg" className="avatar" />3. 属性要用驼峰命名
// ❌ HTML 写法 class="avatar" stroke-width="2" // ✅ JSX 写法 className="avatar" strokeWidth="2"💡 例外:
aria-*和data-*属性保持原样不用改。
三、Props —— 父子通信的桥梁
传递 Props
// 父组件:像传 HTML 属性一样传 <Avatar person={{ name: 'Klaus', imageId: 'abc' }} size={100} />接收 Props
// 子组件:通过解构读取 function Avatar({ person, size }) { return ( <img className="avatar" src={getImageUrl(person)} alt={person.name} width={size} height={size} /> ); }Props 的关键特性
| 特性 | 说明 |
|---|---|
| 单向流动 | 只能从父 → 子,不能反过来 |
| 不可变性 | 子组件不能修改 props |
| 默认值 | function Avatar({ size = 100 }) {} |
| 展开语法 | <Avatar {...props} />一键转发所有 prop |
| children | 嵌套的内容自动成为childrenprop |
// children 示例:Card 不知道里面装了什么 <Card> <Avatar /> {/* 这变成了 Card 的 children prop */} </Card>四、State —— 组件的记忆力
为什么普通变量不够?
// ❌ 这样不行! function Gallery() { let index = 0; // 每次重新渲染都归零 function handleClick() { index = index + 1; // 改了也不会触发重新渲染! } return <button onClick={handleClick}>Next</button>; }两个问题:
- 局部变量无法在多次渲染间持久保存
- 修改变量不会触发 React 重新渲染
用 useState 解决
import { useState } from 'react'; function Gallery() { const [index, setIndex] = useState(0); // 👈 这才是 state function handleClick() { setIndex(index + 1); // 调用 setter → 触发重新渲染 } return <button onClick={handleClick}>Next</button>; }useState 的工作原理
const [count, setCount] = useState(0); │ │ │ │ │ └─ 初始值(只在第一次生效) │ └─ setter 函数(调它才触发更新) └─ state 变量(读当前值)流程:
- 首次渲染 →
useState(0)返回[0, setCount] - 点击按钮 → 调用
setCount(count + 1) - React 记住新值 → 重新执行组件函数
- 第二次渲染 →
useState(0)依然返回[1, setCount](不再是初始值了!)
多条 state 怎么管?
const [index, setIndex] = useState(0); // 数字 const [showMore, setShowMore] = useState(false); // 布尔值 const [name, setName] = useState(''); // 字符串原则:不相关的放不同变量,经常一起变的合并成一个对象。
五、事件处理
基本写法
function MyButton() { function handleClick() { alert('你点我了!'); } return ( <button onClick={handleClick}> 点我 </button> ); }⚠️重点:
onClick={handleClick}后面没有小括号!你传递的是函数本身,不是调用结果。
内联箭头函数
<input onChange={(e) => setName(e.target.value)} />六、条件渲染
三种常用姿势:
1. if…else(最直观)
let content; if (isLoggedIn) { content = <AdminPanel />; } else { content = <LoginForm />; } return <div>{content}</div>;2. 三元运算符(在 JSX 内部用)
<div> {isLoggedIn ? <AdminPanel /> : <LoginForm />} </div>3. 短路求和 &&(不需要 else 时)
<div> {isLoggedIn && <AdminPanel />} </div>七、渲染列表
map + key
const products = [ { title: '卷心菜', id: 1 }, { title: '大蒜', id: 2 }, { title: '苹果', id: 3 }, ]; return ( <ul> {products.map(product => ( <li key={product.id}>{product.title}</li> ))} </ul> );key 是什么?
key告诉 React 哪个元素对应哪个数据项- 必须是同级之间唯一的字符串或数字
- 最佳来源:数据库 ID
- 不要用 index 当 key(排序/增删时会出问题)
八、状态提升 —— 共享 state
场景:两个按钮要联动
最初各自独立:
MyApp ├── MyButton (count=0) ← 各记各的 └── MyButton (count=0)解决方案:把 state 提到共同父组件
export default function MyApp() { const [count, setCount] = useState(0); // 👈 state 在这里 function handleClick() { setCount(count + 1); } return ( <div> <MyButton count={count} onClick={handleClick} /> <MyButton count={count} onClick={handleClick} /> </div> ); } function MyButton({ count, onClick }) { // 👈 通过 props 拿到 return ( <button onClick={onClick}>点了 {count} 次</button> ); }数据流变成:
MyApp (拥有 state) ├──↓ count + onClick ↓──→ MyButton └──↓ count + onClick ↓──→ MyButton这就是“状态提升”—— 把 state 放到最近的公共祖先,再通过 props 往下传。
九、Hook 的基本规则
以use开头的函数叫Hook,它们是 React 的"插件接口":
| Hook | 作用 |
|---|---|
useState | 添加 state |
useEffect | 同步外部系统(下一篇讲) |
useContext | 深层传递数据(下一篇讲) |
useReducer | 复杂 state 逻辑(下一篇讲) |
⚠️ Hook 的铁律
- 只能在组件顶层调用—— 不能在 if/for/嵌套函数里用
- 调用顺序必须稳定—— React 靠顺序来追踪每个 state
- 只能从 React 函数调用—— 不能在普通 JS 函数里用
💡 可以把 Hook 想象成在文件顶部 “import React 的特性”,是声明式的,不是命令式的。
🎯 面试高频追问
Q1:React 的组件是什么?
答:组件是一个返回 JSX 的 JavaScript 函数。它可以小到像一个按钮,大到整个页面。自定义组件名首字母必须大写。
Q2:Props 和 State 的区别是什么?
答:Props 像是函数的参数,由父组件传入,子组件只读不可改;State 像是组件自己的内存,组件可以自己读写,变化会触发重新渲染。
Q3:为什么列表渲染需要 key?
答:key 帮助 React 识别哪些元素发生了变化、新增或删除了。没有 key,React 只能通过位置来判断,导致性能差且容易出错。
Q4:什么是状态提升?
答:当多个组件需要同步数据时,把 state 提升到它们的最近公共父组件中,然后通过 props 向下传递。这是 React 单向数据流的体现。
Q5:useState 和普通的 let 变量有什么区别?
答:普通变量在每次渲染时都会被重置,而且修改它不会触发重新渲染;useState 能在渲染之间持久保存值,调用 setter 会让 React 重新执行组件函数并更新 DOM。
📝 常见面试变体
- “React 中 props 有哪些特点?”
- “如何理解 React 的单向数据流?”
- “为什么不要在循环或条件中使用 Hook?”
- “简述 React 组件的生命周期(渲染过程)”
- “什么情况下需要使用 useState?”
✅ 总结
React 的快速入门核心要点:
- 组件是返回 JSX 的函数,构成 UI 的基本单元 🔥
- JSX是在 JS 中写标签的语言,有三条铁律(单根、闭合、驼峰)
- Props是父传子的单向数据通道,不可变
- State是组件的记忆,通过
useState管理,变化触发重新渲染 - Hook是以
use开头的特殊函数,只能在组件顶层调用 - 状态提升是实现组件间共享数据的核心模式
- 列表渲染用
map()+key,key 要在同级唯一
掌握这些基础后,就可以进一步学习 Effect、Context、Reducer 等进阶功能了。