在本文中,我们将从零开始搭建一个基于 Vite + React + Stylus + Bun 的 TodoList(待办事项)应用。
成品展示:
一、项目初始化流程
1. 创建项目
我们使用 Vite 快速创建一个 React 项目:
npm init vite
cd todo-list
这将生成一个基础的 React 项目结构,包含必要的入口文件和配置。
2. 使用 Bun 安装依赖
Bun 是新一代 JavaScript 运行时和包管理器,速度快于 npm 和 pnpm。
如果是多人协作的中大型项目还是用pnpm稳健一点,小编使用bun完全是想体验一下。这里附上传送门:Bun — 快速的一体化 JavaScript 运行时
安装 Bun(通过 npm)
如果你尚未安装 Bun,可以通过 npm 安装:
npm install -g bun
然后进入项目目录,使用 Bun 安装依赖:
bun install
Bun 的优势:极快的依赖安装速度,内置打包工具,未来可能替代 Node.js。
3. 添加 Stylus 支持
Stylus 是一种 CSS 预处理器,语法简洁,支持变量、嵌套、混合等高级功能。
安装 Stylus:
bun add stylus --dev
Vite 默认支持 .styl 文件,只需引入即可生效,无需额外配置插件。
例如,在入口文件中引入全局样式:
// src/index.jsx
import './global.styl'
结论:是的,这种方式是 Vite 对 Stylus 的原生支持方式,无需额外配置即可全局生效。
二、项目结构概览
todo-list/
├── public/
├── src/
│ ├── components/
│ │ ├── TodoForm.jsx
│ │ ├── TodoItem.jsx
│ │ └── TodoList.jsx
│ ├── hooks/
│ │ └── useTodos.jsx
│ ├── App.jsx
│ ├── index.jsx
│ └── global.styl
├── package.json
└── vite.config.js
三、React 18 入口配置(index.jsx)
Vite 默认生成的是 main.jsx,但我们使用 React 18 推荐的新入口方式 createRoot 替代旧的 ReactDOM.render()。
// src/index.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
import './global.styl'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
)
知识点解析:
StrictMode: 检测潜在问题,如不安全生命周期、废弃 API。createRoot: React 18 新增方法,支持并发模式。./global.styl: 全局样式文件,自动生效。./index.css: 可以删除或保留。
四、组件实现详解(含注释)
1. App.jsx
import { Todos } from './components/Todos'
export default function App() {
return (
<div>
<h1>React TodoList</h1>
<Todos /> {/* 主组件,整合所有子组件 */}
</div>
)
}
2. TodoForm.jsx
import { useState } from 'react'
export const TodoForm = ({ onAddTodo }) => {
const [text, setText] = useState('')
const handleSubmit = (e) => {
e.preventDefault()
const trimmedText = text.trim()
if (!trimmedText) return // 防止空内容提交
onAddTodo(trimmedText)
setText('')
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="输入任务内容"
/>
<button type="submit">添加</button>
</form>
)
}
负责新增待办项,绑定输入状态并触发父组件回调。
3. TodoItem.jsx
export const TodoItem = ({ todo, onToggle, onDelete }) => {
const { id, text, isCompleted } = todo
return (
<div className="todo-item">
<input
type="checkbox"
checked={isCompleted}
onChange={onToggle} // 切换完成状态
/>
<span className={isCompleted ? 'completed' : ''}>{text}</span>
<button onClick={onDelete}>删除</button>
</div>
)
}
展示单个待办项,并提供完成/删除操作。
4. TodoList.jsx
import { TodoItem } from './TodoItem'
export const TodoList = ({ todos, onToggle, onDelete }) => {
return (
<div className="todo-list">
{todos.length > 0 ? (
todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={() => onToggle(todo.id)} // 包装为函数传递id
onDelete={() => onDelete(todo.id)} // 同上
/>
))
) : (
<p>暂无待办事项</p>
)}
</div>
)
}
渲染所有待办项列表,判断是否为空。
5. Todos.jsx
import { useTodos } from '../hooks/useTodos'
export const Todos = () => {
const { todos, addTodo, toggleTodo, deleteTodo } = useTodos()
return (
<div>
<h2>我的待办事项</h2>
<TodoForm onAddTodo={addTodo} />
<TodoList
todos={todos}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
</div>
)
}
整合表单与列表,是整个 TodoList 的主组件。
五、自定义 Hook:useTodos
import { useState, useEffect } from 'react'
export const useTodos = () => {
const [todos, setTodos] = useState(
JSON.parse(localStorage.getItem('todos')) || []
)
const addTodo = (text) => {
setTodos([
...todos,
{ id: Date.now(), text, isCompleted: false }
])
}
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, isCompleted: !todo.isCompleted }
: todo
))
}
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id))
}
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos))
}, [todos])
return { todos, addTodo, toggleTodo, deleteTodo }
}
封装状态逻辑,使得 UI 更加清晰,也便于复用。
六、数据存储:localStorage
localStorage 是浏览器提供的本地存储机制,用于保存键值对数据,刷新页面后不会丢失。
示例:
localStorage.setItem('todos', JSON.stringify(todos))
const storedTodos = JSON.parse(localStorage.getItem('todos'))
优势:
- 数据持久化,页面刷新不丢失
- 不需要服务器参与
- 易于使用,适合小型项目的数据存储
注意:不适合敏感数据或大量数据存储。
刷新后不丢失,查看方式如图所示

七、Bun 与 pnpm、npm 的简单对比
| 特性 | npm | pnpm | Bun |
|---|---|---|---|
| 安装速度 | 慢 | 较快 | 极快 |
| 包管理器 | 原生 npm | 硬链接优化 | 内置 JS 运行时 |
| 构建能力 | 无 | 无 | 支持打包、构建 |
| 安装命令 | npm install | pnpm install | bun install |
| 启动命令 | npm run dev | pnpm dev | bun dev |
✅bun不只是包管理器,它野心很大,但本篇仅把它当作包管理器用
八、项目亮点与难点分析
项目亮点
| 亮点 | 描述 |
|---|---|
| Vite 构建工具 | 快速启动、热更新,提升开发体验 |
| Bun 包管理器 | 极速安装依赖,内置 JS 运行时 |
| Stylus 样式方案 | 支持变量、嵌套、混合等高级特性 |
| useTodos 自定义 Hook | 封装状态逻辑,组件更聚焦 UI |
| localStorage 持久化 | 数据刷新不丢失,用户体验更佳 |
| 路径别名优化 | 减少路径嵌套,提高代码可读性 |
项目难点
| 难点 | 描述 |
|---|---|
| 跨组件通信 | props 层层传递,可考虑用 useContext 或 Zustand 优化 |
| 状态共享限制 | 当前未使用全局状态管理,大型项目中可能需引入 Redux/Zustand |
九、结语
本项目完整实现了:
- 使用 Vite + React + Stylus + Bun 构建现代前端应用
- 组件结构清晰,职责分明
- 使用
localStorage实现数据持久化 - 自定义 Hook 提升代码复用性和可维护性
- Stylus 的快速集成与全局样式导入
- Bun 的使用方式与性能优势