大家好,我是锋哥。
前后端分离并不只是“两个仓库、两个端口”这么简单。真正决定项目是否好维护的,往往是:边界是否清晰、契约是否稳定、错误与鉴权是否一致、部署路径是否可重复。本文以 FastAPI 与 Vue 3(Vite)为例,整理一套在真实项目里反复验证过的做法:从目录结构到联调,从安全到发布。
一、我们到底在分离什么
很多人把“前后端分离”理解成技术栈拆分,但更关键的是职责拆分:
- 前端对“用户体验与交互状态”负责:路由、表单、权限展示、加载与错误反馈、性能体验(懒加载、缓存策略等)。
- 后端对“业务规则与数据一致性”负责:鉴权、授权、事务、审计、集成外部系统、对外暴露稳定的 API。
分离之后,团队并行开发的代价是:你必须额外投资在契约与工程化上。如果契约含糊,前后端就会用“口头约定 + 临时兼容”把复杂度推给未来;如果工程化不足,联调、发布、排障都会变得昂贵。
所以本文的“最佳实践”并不是炫技清单,而是一组让系统长期可维护的默认选项:你可以根据团队规模与交付节奏做裁剪,但最好不要在没理由的情况下跳过安全、契约与可观测性这几块。
二、推荐的整体架构(高清示意图)
下面这张图描述的是一种最常见、也最容易运维的拓扑:浏览器只面对一个入口(通常是 Nginx 或同类反向代理),由它把静态资源与 API 流量分开;后端以 ASGI 方式运行 FastAPI,再连接数据库、缓存与外部服务。
读图时建议抓住三条主线:
- 入口统一:线上尽量别让浏览器直接跨域访问“裸后端端口”,而是用同源路径(例如
/api)转发,减少 CORS 与 Cookie 策略的复杂度(并非所有项目都必须,但默认更省心)。 - 契约显式:FastAPI 天然产出 OpenAPI;前端把它当作“接口真相来源”之一,能显著降低联调摩擦。
- 配置外置:前后端的 base URL、允许的源、密钥与连接串都应来自环境变量或密钥管理,而不是写死在代码里。
三、仓库形态:单体仓库还是多仓库
没有绝对正确,只有更匹配组织协作方式的选项:
- 单仓(monorepo) :
frontend/与backend/同仓库,适合小团队或强协同团队。优点是改动联动清晰、CI 可以一次性跑全链路检查;缺点是要约定好边界,避免“顺手跨目录引用”导致耦合。 - 多仓:前后端各自独立发布,适合组织上本就分离的团队。优点是权限与发布节奏更独立;缺点是契约漂移风险更高,更需要 OpenAPI 归档、版本管理与变更沟通。
无论哪种,都建议把“接口变更”当成一次小型发布:至少更新文档、示例请求、以及前端的类型/客户端封装。
四、工程边界与目录:让改动“有地方去”
目录结构的目标不是“好看”,而是让常见任务有固定落点:新增页面、增加接口、补一个鉴权规则、加一个数据访问方法——团队成员不需要讨论“该放哪”。
下图给出一种在实践里很好用的边界:前端以视图与客户端封装为界,后端以路由—服务—数据访问分层为界;中间用契约与约定对齐。
4.1 前端(Vue 3 + Vite)建议目录(示例)
frontend/
src/
api/ # 对后端调用的封装(按领域拆分)
components/ # 可复用组件
views/ # 页面级组件(路由目标)
router/ # 路由与导航守卫
stores/ # Pinia:会话、用户偏好、跨页状态
types/ # TS 类型(可与 openapi-typescript 生成结果合并)
utils/ # 纯函数工具
vite.config.ts
4.2 后端(FastAPI)建议目录(示例)
backend/
app/
main.py # 创建 app、挂载路由、全局异常处理
api/routes/ # 按领域拆分路由模块
schemas/ # Pydantic:入参/出参模型
services/ # 业务规则(尽量无 HTTP 细节)
repositories/ # 数据访问(ORM/SQL)
core/ # 配置、安全工具、常量
tests/
一个容易被忽略但很重要的原则:路由层薄、服务层厚。路由负责解析与校验输入、组装依赖、返回响应;把“如果…那么…”堆在路由函数里,通常意味着你很快会需要重构。
五、契约优先:OpenAPI、类型与错误码
5.1 以后端 OpenAPI 作为协作枢纽
FastAPI 会自动生成 OpenAPI(默认 /openapi.json)。实践里建议:
- 固定环境可访问:开发、测试环境都能稳定拉到同一份 JSON(或导出为版本化文件)。
- 错误结构尽量统一:例如统一返回
{ "detail": "...", "code": "AUTH_EXPIRED" }(字段名可自定,但要稳定)。 - 破坏性变更要可见:路径、字段、枚举值的变更,至少要在 PR 描述里点名影响面前端哪些页面。
5.2 前端类型从哪里来
常见三种路线,按投入从低到高:
- 手写 TypeScript 类型:最快起步,适合接口很少时。
- 从 OpenAPI 生成类型:接口变多后性价比最高。
- 生成客户端 SDK:最强约束,但生成物需要团队约定使用方式。
无论哪条路,都建议前端只通过 src/api/* 访问后端,避免在组件里散落裸 fetch,否则错误处理与鉴权注入很难统一。
六、鉴权与会话:JWT 的典型落地方式
前后端分离里,Bearer Token(常见为 JWT)非常普遍:无状态、易横向扩展。但它也带来了刷新令牌、吊销、泄露面等问题,需要配套策略(短期 access token、refresh 流程、登出策略、关键操作二次验证等)。
下面是一张“最常见、也最容易讲清楚”的登录与访问受保护资源的流程图(省略刷新令牌细节,可按业务扩展节点):
工程上建议你再补两类“最佳实践细节”:
- 前端:Axios/fetch 拦截器统一附加 Token;对 401 做收敛处理(避免每个页面各自弹窗)。
- 后端:把“取当前用户”做成依赖项(
Depends(get_current_user)),权限判断集中在少量位置,而不是每个路由复制粘贴。
七、Vue 3 前端实践:环境变量、请求层与状态
7.1 环境变量:Vite 的 VITE_ 前缀
前端访问后端地址不要写死域名。Vite 约定只有 VITE_ 开头的变量会暴露到浏览器端:
# frontend/.env.development
VITE_API_BASE_URL=http://127.0.0.1:8000
// frontend/src/api/http.ts(示意)
export const apiBase = import.meta.env.VITE_API_BASE_URL;
7.2 Pinia 放什么、不放什么
- 适合放 Pinia:登录态、用户信息、全局 UI 状态(侧边栏折叠)、跨页缓存的列表筛选条件。
- 不适合硬塞 Pinia:能由 URL 表达的页面状态(尽量交给路由 query),以及只属于单个页面的临时表单中间态。
7.3 路由守卫与权限
路由守卫很适合做“是否已登录”的硬门槛;但细粒度权限(按钮级)仍建议以后端授权为准:前端隐藏只是体验优化,不能当安全边界。
八、FastAPI 后端实践:CORS、异常与分层
8.1 CORS:别把它当成安全机制
开发期你可能需要:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"], # 按环境配置
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
线上若走同源反向代理(/api 转发),浏览器侧往往是同源请求,CORS 压力会小很多;但无论哪种,真正的权限控制都要发生在后端。
8.2 全局异常处理器:让前端“看得懂”
建议把业务异常映射成稳定结构(含错误码),把未预期异常记录日志并返回泛化信息,避免堆栈泄露。
8.3 API 版本与兼容
当移动端、第三方集成或长期演进出线时,版本化能救命:/api/v1/... 或在 Header 里声明版本。最佳实践是:默认就为变更留余地,而不是等到“已经拆不动了”再补。
九、本地开发与联调:怎样少踩坑
本地开发最常见的问题不是代码写不出来,而是路径、代理、环境变量三者没对齐。下面用流程图概括一种稳妥的本地工作方式:
两种模式可以二选一,团队内务必统一:
- Vite dev proxy:前端请求
/api/...,开发服务器转发到127.0.0.1:8000,浏览器侧像同源。 - 直连后端:前端请求完整 URL,更贴近某些线上形态,但要认真配置 CORS 与 Cookie(若使用)。
十、构建、部署与线上流量路径(流程图)
线上部署没有标准答案,但“可回滚、可观测、可重复构建”是共同底线。下图描述一种常见且清晰的发布路径:
十一、质量与协作:CI、测试、评审清单
把“最佳实践”落到团队习惯上,通常靠 CI 与清单,而不是靠自觉:
-
后端:至少覆盖关键 service 的单测;对核心 API 做少量集成测试(启动 TestClient)。
-
前端:对复杂表单校验、权限路由、关键 store 行为加测试(按投入选择 Vitest 等)。
-
CI:格式化、静态检查、测试、构建四件事尽量齐全;PR 小而频,比大而稀更利于审查。
-
评审清单(简版) :
- 是否引入破坏性 API 变更?前端是否同步?
- 鉴权是否在服务端落实?
- 错误信息是否对用户友好、对排障足够?
- 是否新增敏感信息进仓库?
十二、结语:最佳实践为什么总要“因地制宜”
前后端分离的本质收益是并行与清晰边界;成本是契约治理与工程化。FastAPI 与 Vue 3 的组合在开发体验上很合拍:一端擅长结构化 API 与文档,一端擅长组件化与状态管理。
但如果你照搬本文所有建议而不看团队规模,也可能过度工程化。更务实的做法是:先把 安全、契约、错误结构、部署路径 四条底线守住,再按痛点引入类型生成、网关、观测平台等能力。