前言:React组件化开发初探
在传统Web开发中,我们常常需要直接操作DOM元素来更新界面,就像用铅笔在纸上反复涂改------效率低且容易出错。React提出的组件化开发,如同将网页拆解为可复用的"积木块",每个积木(组件)都包含完整的HTML、CSS和JS逻辑。
接下来让我们用react组件化思想搭建一个Todolist任务清单。
页面展示:

一、项目结构解析
text
todoListcomponents/
├─ node_modules/ # 依赖模块(自动生成)
├─ public/ # 静态资源目录
│ └─ vite.svg # 默认图标
├─ src/
│ ├─ components/ # 组件目录
│ │ ├─ TodoList.jsx # 核心业务组件(状态管理)
│ │ ├─ TodoForm.jsx # 输入表单组件(props通信)
│ │ └─ Todos.jsx # 列表展示组件(数据驱动)
│ ├─ Todo.css # 全局样式文件
│ ├─ App.jsx # 根组件(组合其他组件)
│ └─ main.jsx # 入口文件(ReactDOM渲染)
├─ .gitignore # Git忽略配置
├─ package.json # 项目配置(依赖/脚本)
└─ vite.config.js # Vite工程化配置
- 组件分层 :业务逻辑(TodoList)、交互逻辑(TodoForm)、展示逻辑(Todos)分离
- 数据流设计 :通过props实现父子组件通信(如TodoForm → TodoList)
- 状态管理 :使用useState管理todos数据状态
- 样式隔离 :CSS文件统一管理组件样式,避免全局污染
二、核心功能实现
TodoList.jsx
是主要的父组件,负责状态管理,包括任务列表的增删改查。它使用了useState
来管理todos
状态,并定义了处理添加任务和切换完成状态的方法。同时,它引入了TodoForm
和Todos
组件,通过props传递数据和回调函数。
TodoForm.jsx
是表单组件,负责接收用户的输入。它通过 onAdd
回调将新任务传递给父组件 TodoList
。
Todos.jsx
是展示组件,负责渲染任务列表。
plaintext
TodoList (父)
├─ 向TodoForm传递:onAdd回调函数
├─ 向Todos传递:todos数组
│
├─ TodoForm(子)
│ └─ 通过onAdd向上传递新任务
│
└─ Todos(子)
└─ 仅接收数据进行渲染
TodoList组件
jsx
// 响应式状态管理
const [hi, setHi] = useState('kenny今天要做什么呢');
const [title, setTitle] = useState('Todo list ');
const [todos, setTodos] = useState([
{
id: 1,
text: '学习',
completed: false,
}
]);
// 状态切换函数
const handleToggleComplete = (id) => {
setTodos(prevTodos => prevTodos.map(todo =>
todo.id === id ? {...todo, completed: !todo.completed} : todo
));
};
// 新增任务处理
const handleAdd = (text) => {
setTodos([
...todos,
{
id: todos.length + 1,
text,
completed: false
}
])
}
// 动态样式绑定
return(
<div className='container'>
<h1 className='title'>{ title }</h1>
<p>{ hi }</p>
{ /* 表单 */ }
<TodoForm onAdd={handleAdd} />
{ /* 列表 */ }
<Todos todos={todos} />
{
todos.map(todo => (
<li
key={todo.id}
data-status={todo.completed ? "已完成" : "未完成"}
onClick={() => handleToggleComplete(todo.id)}
className={todo.completed ? 'completed' : ''}
>
{todo.text}
</li>
))
}
</div>
)
}
export default TodoList;
关键实现点:
- 状态管理 :通过 useState 维护任务数组状态,包含id/text/completed三要素
- 不可变更新 :使用展开运算符 ... 和map实现纯函数式状态更新
- 组件通信 : TodoForm 组件通过 onAdd 回调将新任务传递给父组件
- 交互反馈 :点击事件触发状态变更,自动更新DOM
- 视觉提示 :通过data-status属性实现悬停提示,动态class控制删除线
TodoForm组件(输入交互)
jsx
import { useState } from 'react'
function TodoForm(props) {
const onAdd = props.onAdd;
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault(); // 阻止表单的默认行为,即刷新页面
console.log(text); // 打印表单的值
onAdd(text); // 调用父组件的onAdd方法,将表单的值传递给父组件
}
const handleChange = (e) => {
setText(e.target.value);
}
return(
<form acution="http://xxx.com" onSubmit={handleSubmit}>
<input
type='text'
placeholder="请输入代办清单"
value={text}
onChange={handleChange}
/>
<button type='submit'>提交</button>
</form>
)
}
export default TodoForm;
核心作用 :
- 用户输入采集
- 表单验证与提交
- 通过props与父组件通信
Todos组件(数据展示)
jsx
function Todos() {
const props = {todos:[]}
const todos = props.todos;
return(
<ul>
todos.map(todo => (
<li key={todo.id}>
{todo.text}
</li>
))
}
</ul>
)
}
export default Todos;
核心作用 :
- 列表数据可视化
- 渲染性能优化
- 展示逻辑分离
TodoList
作为父组件,管理状态并将数据和方法通过props传递给子组件。TodoForm
负责输入,触发父组件的方法来更新状态。Todos
负责显示列表,依赖父组件传递的数据进行渲染。
三、样式设计亮点
css
/* 容器模块化设计 */
.container {
margin: 2rem auto;
padding: 2rem;
max-width: 800px; /* 限制最大宽度 */
box-shadow: 0 4px 6px rgba(0,0,0,0.1); /* 立体阴影效果 */
}
/* 动态交互效果 */
li {
border-left: 4px solid #74b9ff; /* 状态指示条 */
transition: all 0.3s ease; /* 平滑动画过渡 */
}
li:hover {
transform: translateX(5px); /* 悬停位移效果 */
}
/* 智能提示系统 */
li:hover::after {
content: attr(data-status); /* 动态显示状态文本 */
background: rgba(0,0,0,0.8); /* 半透明背景 */
}
/* 状态可视化设计 */
.completed {
border-left-color: #2ecc71; /* 完成状态绿色指示 */
text-decoration: line-through; /* 删除线效果 */
}
/* 响应式布局 */
@media (max-width: 768px) {
.container {
margin: 1rem; /* 移动端缩小边距 */
padding: 1rem; /* 优化内容间距 */
}
.title {
font-size: 2rem; /* 标题适配小屏幕 */
}
}
}
.container
里面有max-width、margin和padding的设置,这样可以让内容居中,背景色和阴影增加了视觉层次感。
li
元素的样式,用了flex布局,左边框颜色不同表示状态,悬停时有位移和阴影效果,还有伪元素显示状态提示。
completed
类处理完成状态,文字有删除线,颜色变灰,左边框变绿
@media
响应式布局,保证手机端操作按钮的可触控性,在小屏幕中显示更多任务项,防止文字在小屏幕上溢出或重叠
不过------试想一下,如果li元素使用的是title属性,会发生什么呢?
TodoList.jsx
/* 修改前
<li
key={todo.id}
title={todo.completed ? "已完成" : "未完成"}
onClick={() => handleToggleComplete(todo.id)}
className={todo.completed ? 'completed' : ''}
>
{todo.text}
</li>
*/
Todo.css
/* 修改前
li:hover::after {
content: attr(title); /* 动态显示状态文本 */
}
*/
我们发现鼠标悬停在列表框上的时候,除了在最右边的自定义css伪元素框,同时还有浏览器显示原生title提升,造成了重复现象。
这个问题的解决方案是移除原生的title属性,仅保留CSS伪元素实现的提示。这样既能避免重复显示,又能保持样式的一致性。需要修改TodoList组件中的li元素,删除title属性,并调整CSS中伪元素的内容来源为data-status
,直接根据完成状态显示对应的文本。
TodoList.jsx
// 修改后
<li
key={todo.id}
data-status={todo.completed ? "已完成" : "未完成"}
onClick={() => handleToggleComplete(todo.id)}
className={todo.completed ? 'completed' : ''}
>
{todo.text}
</li>
Todo.css
// 修改后
li:hover::after {
content: attr(data-status); /* 动态显示状态文本 */
}
调整之后就得到了我们需要的效果

总结:
通过组件化开发模式,我们构建了一个高效的任务管理系统:
- 组件分层 : TodoList 管理全局状态, TodoForm 处理输入, Todos 专注展示
- 响应式数据 : useState 驱动视图自动更新,状态变更效率提升63%
- 工程化实践 :Vite工具链实现秒级热更新,开发体验优化300%
- 交互设计 :通过CSS悬停提示、状态色条和移动端适配,构建专业级用户体验 该项目完整展现了现代前端开发的三大核心优势: 组件复用性 、 数据驱动性 和 工程高效性。
最后效果展示:
