引言
在前端框架百花齐放的今天,React 依然是构建现代 Web 应用的首选工具之一。它不仅仅是一个 UI 库,更是一种设计哲学的体现 —— 声明式编程、组件化架构、单向数据流和不可变性优先。
本文将以一个基于 React + Vite + Stylus 构建的经典 Todo 应用为切入点,深入剖析 React 的核心思想与最佳实践,带你从“会写”迈向“写好”,真正理解 React 背后的设计逻辑。
🧱 项目概览:极简却完整的 Todo 应用
这个 Todo 应用虽小,却五脏俱全,完美诠释了 React 的开发范式:
src/
├── components/
│ ├── TodoInput.jsx # 输入框(添加任务)
│ ├── TodoList.jsx # 列表展示(勾选/删除)
│ └── TodoStats.jsx # 统计信息(总数/完成数)
└── App.jsx # 父组件(状态中枢)
技术栈组合:
- React 18+:函数组件 + Hooks
- Vite:极速启动,热更新体验拉满
- Stylus:优雅的 CSS 预处理器
- localStorage:实现数据持久化
✨ 组件设计:单一职责的艺术
React 的精髓在于 组件化思维 —— 将 UI 拆分为独立、可复用、高内聚的单元。
1. TodoInput:专注输入,隔离状态
const TodoInput = ({ onAdd }) => {
const [inputValue, setInputValue] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!inputValue.trim()) return;
onAdd(inputValue);
setInputValue('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="请输入待办事项..."
/>
<button type="submit">添加</button>
</form>
);
};
✅ 设计亮点:
- 内部状态私有化(
inputValue不暴露) - 仅通过
onAdd回调与父组件通信,接口清晰 - 表单提交阻止默认行为,用户体验更佳
💡 这正是 “受控组件” 的典型应用:视图由状态驱动,而非直接操作 DOM。
2. TodoList:列表渲染与交互处理
const TodoList = ({ todos, onDelete, onToggle }) => {
if (todos.length === 0) {
return <li className="empty">暂无待办事项</li>;
}
return (
<ul className="todo-list">
{todos.map((todo) => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<label>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
</label>
<button onClick={() => onDelete(todo.id)}>删除</button>
</li>
))}
</ul>
);
};
✅ 关键点:
- 使用
key提升列表渲染性能 - 完成状态通过
className动态控制样式 - 所有操作均通过回调函数通知父组件,子组件不修改状态
3. TodoStats:纯展示组件的典范
const TodoStats = ({ total, active, completed, onClearCompleted }) => {
return (
<div className="todo-stats">
<p>Total: {total} | Active: {active} | Completed: {completed}</p>
{completed > 0 && (
<button onClick={onClearCompleted} className="clear-btn">
清除已完成
</button>
)}
</div>
);
};
✅ 它没有内部状态,也不处理业务逻辑,是典型的 “无状态组件” 或 “展示型组件”。
⛓️ 数据流模式:单向数据流的胜利
React 与 Vue 最根本的区别之一就是 数据流向。
Vue 支持双向绑定(v-model),而 React 坚持 单向数据流(Unidirectional Data Flow) —— 数据从父组件流向子组件,子组件只能通过回调函数“请求”变更。
父组件 App:状态的唯一所有者
function App() {
const [todos, setTodos] = useState(() => {
const saved = localStorage.getItem('todos');
return saved ? JSON.parse(saved) : [];
});
// 添加任务
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
// 删除任务
const deleteTodo = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
// 切换完成状态
const toggleTodo = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
// 清除已完成
const clearCompleted = () => {
setTodos(todos.filter((todo) => !todo.completed));
};
// 统计计算
const total = todos.length;
const active = todos.filter((t) => !t.completed).length;
const completed = total - active;
return (
<div className="todo-app">
<h1>My Todo List</h1>
<TodoInput onAdd={addTodo} />
<TodoList
todos={todos}
onDelete={deleteTodo}
onToggle={toggleTodo}
/>
<TodoStats
total={total}
active={active}
completed={completed}
onClearCompleted={clearCompleted}
/>
</div>
);
}
🔄 兄弟组件通信:通过父组件中转
三个子组件互为兄弟,它们之间如何通信?
答案是:不直接通信,而是通过父组件协调。
例如:
- 用户在
TodoInput中点击“添加” - 触发
onAdd→App更新todos App重新计算统计值并传递给TodoStatsTodoStats自动刷新显示
这种 “状态提升 + 响应式更新” 模式确保了:
- 数据来源唯一(Single Source of Truth)
- 状态变更可追溯
- 避免组件间强耦合
🎯 正如 Dan Abramov 所言:“If two components need the same state, lift it up.”
💾 数据持久化:useEffect 的正确打开方式
为了让用户刷新页面后数据不丢失,我们使用 useEffect 同步到 localStorage:
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]); // 仅当 todos 变化时执行
同时初始化状态时读取本地数据:
const [todos, setTodos] = useState(() => {
const saved = localStorage.getItem('todos');
return saved ? JSON.parse(saved) : [];
});
✅ 使用 函数式初始化,避免每次渲染都读取 localStorage,性能更优。
🚀 React 核心思想总结
| 概念 | 说明 | 优势 |
|---|---|---|
| 声明式 UI | 描述“应该是什么”,而非“如何做” | 更易读、更少 bug |
| 组件化 | 拆分 UI 为独立模块 | 可复用、易维护 |
| 单向数据流 | 父传子 props,子通过回调通知父 | 数据流向清晰 |
| 不可变性 | 总是创建新对象,而非修改原对象 | 易于调试、支持时间旅行 |
| 副作用分离 | useEffect 处理副作用(如 localStorage) | 主逻辑纯净 |
🛠️ 最佳实践建议
-
保持组件简单
每个组件只做一件事,避免“上帝组件”。 -
合理使用 Hooks
useState:管理本地状态useEffect:处理副作用useMemo/useCallback:优化性能
-
类型安全加持
使用 TypeScript 或 PropTypes 校验 props 类型,提前发现错误。 -
性能优化技巧
const activeCount = useMemo(() => todos.filter(t => !t.completed).length, [todos] ); -
拥抱现代生态
- 状态管理:Zustand(轻量)、Redux Toolkit(复杂)
- 表单:React Hook Form + Zod
- 路由:React Router
- 测试:Vitest + Testing Library
🔮 未来趋势展望
-
React Server Components (RSC)
服务端组件正在重塑全栈开发,减少客户端 JS 体积,提升首屏性能。 -
并发渲染(Concurrent Mode)
React 18 的自动批处理、过渡动画优化,让应用更流畅。 -
AI 辅助开发
AI 工具(如 GitHub Copilot)已能生成 React 组件,提升开发效率。 -
微前端与模块联邦
大型项目中,React 与其他框架共存成为常态。
📌 结语:从 Todo 学透 React
“你可以用任何框架写 Hello World,但只有真正理解其设计哲学,才能写出可维护的大型应用。”
这个简单的 Todo 应用,实际上浓缩了 React 的灵魂:
- 组件是乐高积木
- 状态是唯一的真相
- 数据流动如河流般单向
- 每一次更新都是对 UI 的重新描述
掌握这些原则,你不仅能写出更好的 React 代码,更能培养出清晰的前端架构思维。