一、React 是什么?
React 是由 Facebook 开发的用于构建用户界面的 JavaScript 库。它采用组件化思想,让 UI 开发更高效、可复用、易维护。
核心特点:
- 组件化开发
- 虚拟 DOM(高性能)
- 单向数据流
- JSX 语法(JavaScript + HTML)
二、标准 React SPA 项目
SPA(Single Page Application)单页应用,整个应用只有一个 HTML 页面(通常是 index.html)。
页面切换时,不刷新整个页面,而是通过 JavaScript 动态替换部分内容(如使用 React Router)。
用户体验更流畅,类似原生 App。首次加载会下载所有(或大部分)JS/CSS 资源,后续路由跳转无需重新请求 HTML。
标准项目结构如下:
javascript
my-react-app/
├── public/
│ └── index.html ← 唯一的 HTML 入口(SPA 核心)
├── src/
│ ├── App.jsx ← 根组件
│ ├── main.jsx ← 应用入口(渲染到 #root)
│ ├── components/ ← 可复用 UI 组件
│ ├── pages/ ← 页面级组件(配合路由)
│ ├── hooks/ ← 自定义 Hook
│ ├── assets/ ← 图片、字体等静态资源
│ └── index.css ← 全局样式
├── package.json ← 依赖和脚本
└── vite.config.js ← 构建配置(若使用 Vite)
三、核心概念速览
1. JSX 语法 ------ JavaScript 中写 HTML
jsx
function App() {
return <h1>Hello, React!</h1>;
}
注意:JSX 中 class 要写成
className
,事件用驼峰如onClick
2. 组件 ------ UI 的构建块
函数组件(推荐)
jsx
function Welcome(props) {
return <h2>Hello, {props.name}</h2>;
}
// 使用组件
function App() {
return (
<div>
<Welcome name="Alice" />
<Welcome name="Bob" />
</div>
);
}
3. 状态管理 ------ useState Hook
jsx
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // 初始化状态
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>
点我加一
</button>
</div>
);
}
✅
useState
返回一个状态变量和更新它的函数
4. 生命周期 & 副作用 ------ useEffect Hook
jsx
import { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// 清理函数:组件卸载时执行
return () => clearInterval(timer);
}, []); // 空数组 = 只在组件挂载时执行一次
return <div>已运行:{seconds} 秒</div>;
}
✅
useEffect
用于处理副作用(如数据获取、订阅、定时器等)
在 React 中,"副作用"指的是组件渲染之外的操作,比如:
- 发起网络请求(fetch API)
- 订阅事件(WebSocket、键盘事件)
- 操作 DOM(非 React 控制的元素)
- 启动定时器(setInterval / setTimeout) ← 你这个例子!
- 修改全局变量或文档标题
这些操作不应该在组件主体内直接执行(会导致每次渲染都重复执行、内存泄漏等),而应该放在 useEffect 中管理
5. 条件渲染 & 列表渲染
jsx
function TodoList({ todos }) {
if (todos.length === 0) {
return <p>暂无待办事项</p>;
}
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
✅ 列表必须加
key
(唯一标识),提升性能和稳定性
四、实战小项目:
创建一个简单的待办事项应用,包含添加、删除、显示功能。
1. 创建 Todo 组件
jsx
// src/components/TodoList.jsx
import { useState } from 'react';
export default function TodoList() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
const addTodo = () => {
if (inputValue.trim()) {
setTodos([
...todos,
{ id: Date.now(), text: inputValue, completed: false }
]);
setInputValue('');
}
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<h2>我的待办事项</h2>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="输入待办事项"
/>
<button onClick={addTodo}>添加</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</li>
))}
</ul>
</div>
);
}
2. 在 App.jsx 中使用
jsx
// src/App.jsx
import TodoList from './components/TodoList';
function App() {
return (
<div className="App">
<TodoList />
</div>
);
}
export default App;
✅ 运行项目,你就能添加和删除待办事项了!
五、进阶概念
1. 样式处理
- 使用 CSS Modules:
Button.module.css
- 或 Tailwind CSS / Styled-components
CSS Modules:一种在 组件级别 引入 CSS 样式的方案,它通过构建工具(如 Vite、Webpack)在编译时自动为类名生成唯一哈希值,从而实现:
2. 路由(React Router)
路由(Routing):根据不同的 URL 地址,显示不同的页面内容,而无需刷新整个网页。
bash
npm install react-router-dom
jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
3. 状态管理进阶
- 小项目:
useContext
+useReducer
- 大项目:Redux Toolkit / Zustand
4. 数据请求
使用 fetch
或 axios
+ useEffect
jsx
useEffect(() => {
fetch('/api/todos')
.then(res => res.json())
.then(data => setTodos(data));
}, []);
六、总结
概念 | 作用 | 使用方式 |
---|---|---|
组件 | UI 模块化 | 函数组件 + JSX |
useState | 管理状态 | const [state, setState] = useState() |
useEffect | 处理副作用 | useEffect(() => {}, [deps]) |
条件/列表渲染 | 动态展示内容 | {condition && <Comp />} / map() |
Props | 父传子数据 | <Child name="xxx" /> |