共计 4148 个字符,预计需要花费 11 分钟才能阅读完成。
前言
作为刚接触全栈开发的新手,最头疼的莫过于如何将前后端有机结合起来。最近我用 Claude Code 完整走了一遍 Todo List 应用的开发流程,从架构设计到最终部署,收获颇丰。下面就把这个实战过程记录下来,希望能帮到同样在摸索的你。

1. 架构设计:从单体到分离
刚开始学习时,我习惯把前后端代码混在一起(单体架构),但随着功能增加,发现这种架构存在明显问题:
- 前端任何改动都需要重新部署整个应用
- 后端接口调整可能直接破坏前端功能
- 团队协作时互相等待,效率低下
改用前后端分离架构后:
- 前端:专注 UI 和交互,通过 API 获取数据
- 后端:专注业务逻辑和数据持久化
- 两者通过明确定义的接口契约进行通信
这种分离带来的好处是:
- 开发效率提升:前后端可以并行开发
- 技术栈自由:前端可以用 React/Vue,后端可以用 Java/Node
- 易于扩展:可以单独扩展前端或后端服务
2. 技术栈选择
根据 Claude Code 的建议,我选择了这套技术组合:
- 前端:React + TypeScript
- 组件化开发体验好
- TypeScript 提供类型安全
- 后端:Node.js + Express
- JavaScript 全栈统一
- 轻量级框架学习成本低
- 数据库:MongoDB
- 文档型数据库适合 Todo 场景
- 不需要预先定义 schema
3. JWT 鉴权实现
安全是 Web 应用的基础。我们采用 JWT(JSON Web Token) 机制来实现身份验证:
sequenceDiagram
participant 用户
participant 前端
participant 后端
用户 ->> 前端: 输入用户名密码
前端 ->> 后端: POST /api/auth/login
后端 -->> 前端: 返回 JWT 令牌
前端 ->> 前端: 存储 token 到 localStorage
前端 ->> 后端: 携带 token 请求数据 (Authorization 头)
后端 ->> 后端: 验证 token 有效性
后端 -->> 前端: 返回请求的数据
核心代码实现:
// auth.service.ts - 生成 Token
import jwt from 'jsonwebtoken';
export const generateToken = (userId: string): string => {
return jwt.sign({ userId},
process.env.JWT_SECRET!,
{expiresIn: '30d'}
);
};
// auth.middleware.ts - 验证 Token
export const authenticate = (
req: Request,
res: Response,
next: NextFunction
) => {const token = req.header('Authorization')?.replace('Bearer', '');
if (!token) {return res.status(401).json({
code: 'UNAUTHORIZED',
message: '请提供认证 token'
});
}
try {const decoded = jwt.verify(token, process.env.JWT_SECRET!) as {userId: string;};
req.userId = decoded.userId;
next();} catch (err) {res.status(401).json({
code: 'INVALID_TOKEN',
message: '无效的 token'
});
}
};
4. Todo List 核心功能实现
后端 API 设计(符合 OpenAPI 3.0)
paths:
/api/todos:
get:
summary: 获取当前用户的待办列表
security:
- bearerAuth: []
responses:
'200':
description: 待办列表
content:
application/json:
schema:
$ref: '#/components/schemas/TodoList'
post:
summary: 创建新待办
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTodoRequest'
responses:
'201':
description: 创建成功
content:
application/json:
schema:
$ref: '#/components/schemas/TodoItem'
前端组件示例
// TodoList.tsx
import {useState, useEffect} from 'react';
type Todo = {
_id: string;
title: string;
completed: boolean;
createdAt: string;
};
export default function TodoList() {const [todos, setTodos] = useState<Todo[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {const fetchTodos = async () => {
try {
const res = await fetch('/api/todos', {
headers: {'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (!res.ok) throw new Error('获取数据失败');
const data = await res.json();
setTodos(data);
} catch (err) {console.error(err);
} finally {setLoading(false);
}
};
fetchTodos();}, []);
if (loading) return <div> 加载中...</div>;
return (
<div>
<h2> 我的待办 </h2>
<ul>
{todos.map(todo => (<li key={todo._id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo._id)}
/>
{todo.title}
</li>
))}
</ul>
</div>
);
}
5. Docker 化部署
使用 Docker Compose 编排服务,简化部署流程:
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
depends_on:
- backend
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "5000:5000"
environment:
- MONGO_URI=mongodb://mongo:27017/todoapp
- JWT_SECRET=your_secure_secret
depends_on:
- mongo
mongo:
image: mongo:5.0
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
volumes:
mongo-data:
启动命令:
docker-compose up -d --build
6. 生产环境配置
Nginx 反向代理配置
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /api {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
性能优化建议
-
数据库索引:
// 在 MongoDB 中为常用查询字段创建索引 db.todos.createIndex({userId: 1, completed: 1}); -
接口缓存:
// 使用 redis 缓存热门数据 import redis from 'redis'; const client = redis.createClient(); app.get('/api/todos', async (req, res) => {const cacheKey = `todos:${req.userId}`; client.get(cacheKey, async (err, data) => {if (data) return res.json(JSON.parse(data)); const todos = await Todo.find({userId: req.userId}); client.setex(cacheKey, 3600, JSON.stringify(todos)); // 缓存 1 小时 res.json(todos); }); });
7. 常见问题排查
问题 1 :前端请求后端 API 出现 CORS 错误
解决方法:
// 后端添加 CORS 中间件
import cors from 'cors';
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:3000',
credentials: true
}));
问题 2 :MongoDB 连接失败
检查点:
- MongoDB 服务是否启动
- 连接字符串是否正确
- 网络防火墙是否放行 27017 端口
结语
通过这个 Todo List 项目的完整实践,我深刻体会到前后端分离架构的优势。虽然初期配置可能稍显复杂,但一旦架构搭建完成,后续的开发效率会大幅提升。
建议你在实际开发中:
- 先定义好 API 契约(OpenAPI 规范)
- 前后端并行开发,通过 Mock 数据联调
- 尽早引入 Docker 简化环境配置
- 重视错误处理和日志记录
完整的项目代码已放在 GitHub 上,欢迎参考和指正。希望这篇指南能帮你少走弯路,快速掌握全栈开发的核心技能!
正文完
