全栈开发技能(UI 和后端)
本技能提供了端到端的架构指南、工作流手册和编码标准,用于使用现代 TypeScript/JavaScript 在前端(UI)和后端开发生产级 Web 应用。
1. 核心架构原则
- 端到端类型安全:
- 领域模型和数据传输对象(DTO)单一事实来源。
- 使用模式验证库(如 Zod)在运行时解析和验证请求,并在编译时推断 TypeScript 类型。
- 分层关注点分离:
- 前端: 严格分离展示性原语、业务/容器组件和数据获取钩子。
- 后端: 严格分离路由/控制器(HTTP 传输)、服务层(业务逻辑)和数据访问/仓库层(数据库/ORM)。
- API 契约优先:
- 在实现 UI 或后端细节之前建立请求和响应契约。
- 统一响应信封:
{ success: true, data: T }或{ success: false, error: { message, code, details } }。
- 弹性和错误处理:
- 前端: 错误边界、骨架屏加载状态、优雅降级和用户友好的通知。
- 后端: 集中式错误中间件、操作性错误与程序员错误分类、结构化日志和安全默认值。
- 默认安全:
- 安全标头(Helmet)、明确的 CORS 策略、输入验证/清理、速率限制和安全认证(HTTP-only cookie 或 bearer token)。
2. 推荐目录结构
对于全栈仓库(单体仓库或拆分结构):
├── packages/ or apps/
│ ├── client/ # 前端 UI(React / Next.js / Vite)
│ │ ├── src/
│ │ │ ├── assets/ # 静态资源(图片、图标、字体)
│ │ │ ├── components/ # UI 组件(原子、分子、有机体)
│ │ │ │ ├── ui/ # 核心设计系统原语(Button, Input, Modal)
│ │ │ │ └── features/ # 领域特定复合组件
│ │ │ ├── hooks/ # 自定义 React 钩子(useAuth, useDebounce 等)
│ │ │ ├── services/ # API 客户端和服务集成
│ │ │ ├── store/ # 全局状态管理(Zustand, Context 等)
│ │ │ ├── types/ # 客户端特定类型
│ │ │ ├── utils/ # 辅助函数和格式化工具
│ │ │ ├── App.tsx # 主要应用组件和路由
│ │ │ ├── main.tsx # 入口点
│ │ │ └── index.css # 全局 CSS 和设计令牌
│ │ └── package.json
│ │
│ ├── server/ # 后端 API(Node.js / Express / Fastify)
│ │ ├── src/
│ │ │ ├── config/ # 环境变量和运行时配置
│ │ │ ├── controllers/ # HTTP 路由处理器(提取参数,调用服务)
│ │ │ ├── middleware/ # 认证、验证、错误处理器、速率限制器
│ │ │ ├── models/ # 数据库模型/模式(Prisma, Drizzle 等)
│ │ │ ├── routes/ # Express/Fastify 路由定义
│ │ │ ├── services/ # 业务逻辑和外部 API 交互
│ │ │ ├── utils/ # 后端辅助工具(jwt, logger, crypto)
│ │ │ ├── app.ts # Express/Fastify 应用配置
│ │ │ └── server.ts # 服务器入口点和优雅关闭
│ │ └── package.json
│ │
│ └── shared/ # 共享模式、DTO 和 TypeScript 契约
│ ├── src/
│ │ ├── schemas/ # Zod 模式(user.schema.ts, auth.schema.ts)
│ │ └── types/ # 推断类型(export type User = z.infer<...>)
│ └── package.json
3. 分步开发工作流
实现任何全栈功能时,按顺序执行以下阶段:
阶段 1: 定义共享契约和模式
- 在
shared/或common/中定义表示请求负载和响应实体的 Zod 模式。 - 导出推断的 TypeScript 类型。
import { z } from 'zod';
export const CreateUserSchema = z.object({
email: z.string().email('邮箱地址无效'),
name: z.string().min(2, '姓名至少需要 2 个字符'),
role: z.enum(['admin', 'user']).default('user'),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
export type UserResponse = CreateUserInput & { id: string; createdAt: string };
阶段 2: 构建后端服务和端点
- 验证中间件: 使用 Zod 模式应用验证中间件,以 HTTP 400 早期拒绝格式错误的输入。
- 服务层: 在服务类或模块中编写纯业务逻辑。将数据库查询与 HTTP 概念(
req、res)隔离。 - 控制器: 处理 HTTP 输入提取,调用服务,并使用
sendSuccess()返回标准化响应。 - 错误处理: 使用自定义错误(
NotFoundError、BadRequestError、UnauthorizedError),让集中式错误中间件捕获并格式化响应。
阶段 3: 构建前端 UI 组件
- 设计系统令牌: 确保存在用于颜色、排版、间距和阴影的 CSS 自定义属性或设计系统变量。
- 原子/UI 原语: 构建或复用基础 UI 组件(按钮、输入框、徽章、对话框),并支持键盘导航和 ARIA 属性。
- 功能视图/页面: 组合复合组件,具有清晰的视觉层次、充足的空白、现代排版和响应式网格/弹性布局。
- 状态和微交互:
- 为悬停、激活、焦点可见和禁用状态提供视觉反馈。
- 在等待提交期间包含骨架屏、加载指示器和禁用按钮。
阶段 4: 集成和数据获取
- API 客户端: 使用集中式 fetch 包装器或配置了基本 URL、超时、凭据和拦截器的 Axios 实例。
- 服务器状态: 使用 TanStack Query (React Query) 或标准钩子来查询和变更数据。
- 成功变更后使缓存失效。
- 在适当的情况下使用乐观更新以获得流畅的用户体验。
- 表单处理: 将表单与 React Hook Form + Zod resolver 集成,以在客户端和服务器之间共享完全相同的验证规则。
阶段 5: 验证和加固
- 后端验证: 使用单元/集成测试或 curl/fetch 测试端点,以验证边界情况(无效输入、缺少令牌、重复键)。
- 前端验证: 验证在桌面、平板和移动设备视口宽度下的响应性。
- 安全检查: 确认 CORS、速率限制和认证守卫已激活。
4. 快速代码模式
标准 API 响应辅助函数(后端)
// server/src/utils/response.ts
import { Response } from 'express';
export function sendSuccess<T>(res: Response, data: T, statusCode = 200, meta?: unknown) {
return res.status(statusCode).json({
success: true,
data,
...(meta ? { meta } : {}),
});
}
export function sendError(res: Response, message: string, statusCode = 500, details?: unknown) {
return res.status(statusCode).json({
success: false,
error: {
message,
statusCode,
details: details ?? null,
},
});
}
集中式 Express 错误中间件
// server/src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { ZodError } from 'zod';
export class AppError extends Error {
constructor(public message: string, public statusCode = 500, public details?: unknown) {
super(message);
this.name = 'AppError';
}
}
export function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction) {
if (err instanceof ZodError) {
return res.status(400).json({
success: false,
error: {
message: '验证失败',
statusCode: 400,
details: err.flatten().fieldErrors,
},
});
}
if (err instanceof AppError) {
return res.status(err.statusCode).json({
success: false,
error: {
message: err.message,
statusCode: err.statusCode,
details: err.details ?? null,
},
});
}
console.error('未处理的服务器错误:', err);
return res.status(500).json({
success: false,
error: {
message: '内部服务器错误',
statusCode: 500,
},
});
}
类型安全 API 客户端(前端)
// client/src/services/apiClient.ts
interface ApiResponse<T> {
success: boolean;
data: T;
error?: {
message: string;
statusCode: number;
details?: unknown;
};
}
export async function apiRequest<T>(url: string, options?: RequestInit): Promise<T> {
const res = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
...options,
});
const body: ApiResponse<T> = await res.json();
if (!res.ok || !body.success) {
throw new Error(body.error?.message || `HTTP ${res.status}: 请求失败`);
}
return body.data;
}
5. 参考和深入阅读
有关深入的架构模式,请查阅: