React框架实战:构建一个全栈待办事项应用
在现代Web开发中,React凭借其组件化、声明式和高效的虚拟DOM特性,已成为构建用户界面的首选框架。本文将通过一个完整的实战项目,带你从零开始构建一个全栈待办事项应用,涵盖前端React开发、后端API设计以及数据持久化方案。
项目规划与技术栈选择
在开始编码前,明确项目需求和技术选型至关重要。这个待办事项应用需要实现以下核心功能:
- 创建、读取、更新和删除待办事项
- 标记待办事项为完成/未完成状态
- 按优先级和截止日期排序
- 用户认证与数据隔离
技术栈选择如下:
- 前端:React 18 + TypeScript + Redux Toolkit
- 后端:Node.js + Express + MongoDB
- 认证:JWT (JSON Web Tokens)
- 部署:Docker + AWS EC2
后端开发:构建RESTful API
1. 项目初始化与依赖安装
首先创建Express项目并安装必要依赖:
mkdir todo-backend && cd todo-backend
npm init -y
npm install express mongoose jsonwebtoken bcryptjs cors dotenv
npm install -D nodemon
2. 设计数据模型
使用Mongoose定义待办事项和用户的数据模型:
// models/Todo.js
const mongoose = require(\'mongoose\');
const todoSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true
},
description: String,
completed: {
type: Boolean,
default: false
},
priority: {
type: String,
enum: [\'low\', \'medium\', \'high\'],
default: \'medium\'
},
dueDate: Date,
user: {
type: mongoose.Schema.Types.ObjectId,
ref: \'User\',
required: true
}
}, {
timestamps: true
});
module.exports = mongoose.model(\'Todo\', todoSchema);
3. 实现认证中间件
创建JWT认证中间件保护API端点:
middleware/auth.js
const jwt = require(\'jsonwebtoken\');
require(\'dotenv\').config();
const auth = (req, res, next) => {
const token = req.header(\'Authorization\')?.replace(\'Bearer \', \'\');
if (!token) {
return res.status(401).json({ message: \'No token, authorization denied\' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded.user;
next();
} catch (error) {
res.status(401).json({ message: \'Token is not valid\' });
}
};
module.exports = auth;
4. 编写API路由
实现完整的CRUD操作路由:
// routes/todos.js
const express = require(\'express\');
const router = express.Router();
const auth = require(\'../middleware/auth\');
const Todo = require(\'../models/Todo\');
// @route POST api/todos
// @desc Create a todo
// @access Private
router.post(\'/\', auth, async (req, res) => {
const { title, description, priority, dueDate } = req.body;
try {
const newTodo = new Todo({
title,
description,
priority,
dueDate,
user: req.user.id
});
const todo = await newTodo.save();
res.json(todo);
} catch (error) {
res.status(500).json({ message: \'Server error\' });
}
});
// @route GET api/todos
// @desc Get all todos for a user
// @access Private
router.get(\'/\', auth, async (req, res) => {
try {
const todos = await Todo.find({ user: req.user.id })
.sort({ dueDate: 1, priority: -1 });
res.json(todos);
} catch (error) {
res.status(500).json({ message: \'Server error\' });
}
});
// @route PUT api/todos/:id
// @desc Update a todo
// @access Private
router.put(\'/:id\', auth, async (req, res) => {
const { title, description, completed, priority, dueDate } = req.body;
const todoFields = {};
if (title) todoFields.title = title;
if (description) todoFields.description = description;
if (completed !== undefined) todoFields.completed = completed;
if (priority) todoFields.priority = priority;
if (dueDate) todoFields.dueDate = dueDate;
try {
let todo = await Todo.findById(req.params.id);
if (!todo) return res.status(404).json({ message: \'Todo not found\' });
if (todo.user.toString() !== req.user.id) {
return res.status(401).json({ message: \'Not authorized\' });
}
todo = await Todo.findByIdAndUpdate(
req.params.id,
{ $set: todoFields },
{ new: true }
);
res.json(todo);
} catch (error) {
res.status(500).json({ message: \'Server error\' });
}
});
// @route DELETE api/todos/:id
// @desc Delete a todo
// @access Private
router.delete(\'/:id\', auth, async (req, res) => {
try {
let todo = await Todo.findById(req.params.id);
if (!todo) return res.status(404).json({ message: \'Todo not found\' });
if (todo.user.toString() !== req.user.id) {
return res.status(401).json({ message: \'Not authorized\' });
}
await Todo.findByIdAndRemove(req.params.id);
res.json({ message: \'Todo removed\' });
} catch (error) {
res.status(500).json({ message: \'Server error\' });
}
});
module.exports = router;
前端开发:构建React应用
1. 项目初始化与配置
使用Create React App初始化项目并配置Redux:
npx create-react-app todo-frontend
cd todo-frontend
npm install @reduxjs/toolkit react-redux axios
npm install -D @types/react-router-dom
2. 设计Redux状态管理
使用Redux Toolkit创建状态切片:
// features/todos/todosSlice.js
import { createSlice, createAsyncThunk } from \'@reduxjs/toolkit\';
import axios from \'axios\';
const API_URL = \'/api/todos\';
// Get todos
export const getTodos = createAsyncThunk(\'todos/getTodos\', async (token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
const response = await axios.get(API_URL, config);
return response.data;
});
// Add todo
export const addTodo = createAsyncThunk(\'todos/addTodo\', async ({ todo, token }) => {
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
const response = await axios.post(API_URL, todo, config);
return response.data;
});
// Update todo
export const updateTodo = createAsyncThunk(\'todos/updateTodo\', async ({ id, todo, token }) => {
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
const response = await axios.put(`${API_URL}/${id}`, todo, config);
return response.data;
});
// Delete todo
export const deleteTodo = createAsyncThunk(\'todos/deleteTodo\', async ({ id, token }) => {
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
await axios.delete(`${API_URL}/${id}`, config);
return id;
});
const initialState = {
todos: [],
isLoading: false,
error: null
};
const todoSlice = createSlice({
name: \'todos\',
initialState,
reducers: {
reset: (state) => {
state.todos = [];
state.isLoading = false;
state.error = null;
}
},
extraReducers: (builder) => {
builder
// Get todos
.addCase(getTodos.pending, (state) => {
state.isLoading = true;
})
.addCase(getTodos.fulfilled, (state, action) => {
state.isLoading = false;
state.todos = action.payload;
})
.addCase(getTodos.rejected, (state, action) => {
state.isLoading = false;
state.error = action.error.message;
})
// Add todo
.addCase(addTodo.pending, (state) => {
state.isLoading = true;
})
.addCase(addTodo.fulfilled, (state, action) => {
state.isLoading = false;
state.todos.push(action.payload);
})
.addCase(addTodo.rejected, (state, action) => {
state.isLoading = false;
state.error = action.error.message;
})
// Update todo
.addCase(updateTodo.fulfilled, (state, action) => {
const index = state.todos.findIndex(todo => todo._id === action.payload._id);
if (index !== -1) {
state.todos[index] = action.payload;
}
})
// Delete todo
.addCase(deleteTodo.fulfilled, (state, action) => {
state.todos = state.todos.filter(todo => todo._id !== action.payload);
});
}
});
export const { reset } = todoSlice.actions;
export default todoSlice.reducer;
3. 创建React组件
构建核心UI组件:
// components/TodoList.js
import React from \'react\';
import { useSelector, useDispatch } from \'react-redux\';
import { deleteTodo, updateTodo } from \'../features/todos/todosSlice\';
const TodoList = () => {
const { todos } = useSelector((state) => state.todos);
const dispatch = useDispatch();
const handleDelete = (id) => {
dispatch(deleteTodo({ id, token: localStorage.getItem(\'token\') }));
};
const handleToggle = (todo) => {
dispatch(updateTodo({
id: todo._id,
todo: { completed: !todo.completed },
token: localStorage.getItem(\'token\')
}));
};
return (
{todos.map((todo) => (
{todo.title}
{todo.description}
Priority: {todo.priority}
Due: {new Date(todo.dueDate).toLocaleDateString()}
handleToggle(todo)}
/>
))}
);
};
export default TodoList;
4. 实现路由与布局
配置应用路由和主布局组件:
// App.js
import React from \'react\';
import { BrowserRouter as Router, Routes, Route } from \'react-router-dom\';
import { Provider } from \'react-redux\';
import store from \'./app/store\';
import Login from \'./components/Login\';
import Register from \'./components/Register\';
import Dashboard from \'./components/Dashboard\';
import PrivateRoute from \'./components/PrivateRoute\';
function App() {
return (
<Route path=\"/login\" element={} />
<Route path=\"/register\" element={} />
<Route
path=\"/\"
element={
}
/>
);
}
export default App;
部署与优化
1. Docker化应用
创建Dockerfile实现容器化部署:
# Dockerfile for backend
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD [\"npm\", \"start\"]
# Dockerfile for frontend
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD [\"npm\", \"start\"]
2. 性能优化策略
提升应用性能的关键措施:
- 使用React.memo和useMemo优化组件渲染
- 实现虚拟滚动处理大量待办事项
- 启用Redux的持久化存储保存用户状态
- 添加错误边界组件捕获渲染错误
- 实现离线功能使用Service Worker
总结
通过这个全栈待办事项应用的开发实践,我们深入掌握了React生态系统的高级用法,包括Redux状态管理、JWT认证、RESTful API设计以及现代部署流程。这个项目展示了如何将前端框架与后端服务完美结合,构建出功能完善、性能优秀的Web应用。
在实际开发中,还可以进一步扩展功能,如添加文件上传、实时通知、数据可视化等特性。通过不断实践和优化,React开发者能够构建出更加复杂和强大的应用,满足现代Web应用的各种需求。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...
