FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南”

你写好了丝滑的FastAPI后端,Vue前端也跑得欢,但一联调就炸?

跨域、路由冲突、环境变量混乱......这些问题我当初全踩过。最惨一次,前后端联调花了整整三天,结果只是axios配置里少写了一个斜杠。今天就跟你好好聊聊FastAPI+Vue前后端分离的正确姿势,全是实战经验,不是那种抄官方文档的教程。


📌 文章脉络

🔸 一个让你血压飙升的联调场景

🔸 我的"强迫症"项目结构(直接拿去用)

🔸 通信的3个核心配置(少一个都会炸)

🔸 开发环境 vs 生产环境:别再手改baseURL了!

🔸 那些文档不会告诉你的"隐形坑"

🔸 进阶思考:拆分vs聚合,你的项目适合哪种?

🚨 先还原一个真实"案发现场"

小张花了一周写完FastAPI接口,用Postman测试全部通过,美滋滋地去睡了个好觉。第二天起床,信心满满地启动Vue项目,npm run dev,然后......控制台一片红Access-Control-Allow-Origin 错误。

赶紧上网搜,装了个flask-cors?不对,我是FastAPI。手忙脚乱加上CORSMiddleware,跨域是解决了,但POST请求又报422 Unprocessable Entity------前端传的JSON格式后端不认。

你可能会问:不就是配置个代理、写个接口吗?错!前后端分离的"分离"二字,坑全藏在细节里。下面我把最终稳定运行的结构和配置贴出来,你直接复制粘贴就能跑。

📁 我的"强迫症"项目结构(治好了我的精神内耗)

以前我喜欢把所有文件堆在一个文件夹里,后来发现维护起来像在垃圾堆里找钥匙。现在的结构长这样,按功能拆分明细,但又不至于过度拆分(别学我一开始拆了20个文件夹,结果自己都找不到东西)。

复制代码
fastapi_vue_project/
├── backend/                 # FastAPI后端
│   ├── app/
│   │   ├── api/            # 路由层(按模块分)
│   │   │   ├── v1/
│   │   │   │   ├── users.py
│   │   │   │   └── tasks.py
│   │   ├── core/           # 配置、安全、数据库连接
│   │   │   ├── config.py
│   │   │   └── database.py
│   │   ├── models/         # SQLAlchemy模型
│   │   ├── schemas/        # Pydantic模型(请求/响应结构)
│   │   ├── services/       # 业务逻辑层
│   │   └── main.py         # 入口
│   ├── requirements.txt
│   └── .env                # 环境变量(别提交到git!)
├── frontend/               # Vue3前端
│   ├── src/
│   │   ├── api/           # 封装axios请求
│   │   ├── views/         # 页面
│   │   ├── router/        # 路由
│   │   └── utils/         # 工具函数
│   └── .env.development   # 开发环境变量
│   └── .env.production    # 生产环境变量
└── docker-compose.yml      # 可选,线上部署用

👆 这个结构我用了很久,大小项目都稳得很。关键是 core/config.py 和 **frontend/.env.***这对"黄金搭档",解决了90%的环境切换问题。

🔌 通信的3个核心配置(少一个都连不上)

1️⃣ FastAPI的CORS中间件(不是加一行就完事的)

很多人复制官方示例allow_origins=["*"]就跑了,但生产环境千万别这么干 !而且你还要注意allow_credentialsallow_headers的配合。

复制代码
# backend/app/core/config.py
class Settings:
    BACKEND_CORS_ORIGINS = ["http://localhost:5173", "http://127.0.0.1:5173"]  # 开发环境
    # 生产环境从环境变量读取,不要写死

# backend/app/main.py
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.BACKEND_CORS_ORIGINS,  # 注意!不是 "*"
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["Authorization", "Content-Type"],
)

这里有个坑:如果你前端用axios带上了withCredentials: true,后端allow_origins就不能是["*"],必须指定具体域名。当初我在这里卡了4个小时,最后翻FastAPI源码才找到原因。

2️⃣ Vue的代理配置(开发神器,但别滥用)

Vite(或webpack)的proxy配置简直是开发阶段的救星,让你彻底告别跨域烦恼。但很多人抄完配置就不管了,结果部署到生产环境又报错。

复制代码
// frontend/vite.config.js
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',  // FastAPI默认端口
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')  // 注意这个重写规则!
      }
    }
  }
})        

注意看rewrite这一行:如果你的FastAPI路由是@app.get("/users"),前端请求/api/users,代理会把/api去掉再转发。这里写错了路径,就会404。

3️⃣ 统一的响应格式(别让前端猜你返回什么)

这是我最想吐槽的一点:很多人后端返回的数据结构每天不一样,今天{"data": {...}},明天{"result": {...}}。前端大哥没被你气死算我输。

我的习惯:统一用下面的格式

复制代码
# backend/app/schemas/common.py
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional

T = TypeVar('T')

class ResponseModel(BaseModel, Generic[T]):
    code: int = 200
    message: str = "success"
    data: Optional[T] = None

# 使用示例
@router.get("/users/{user_id}")
def get_user(user_id: int):
    user = service.get_user(user_id)
    return ResponseModel(data=user)

前端axios拦截器统一处理这个结构,代码量直接砍半 。是不是以为这样就完了?不,还有一个关于错误码的约定,建议至少约定401去登录、403无权限、422参数错误,别让前端去猜。

🌍 开发 vs 生产:别再手改baseURL了!

我见过最野的操作:每次部署前,手动把 axios 的 baseURL 从localhost:8000改成线上域名,然后commit,然后......忘了改回来。😱

正确姿势:用环境变量

复制代码
# frontend/.env.development
VITE_API_BASE_URL = '/api'   # 开发走代理

# frontend/.env.production
VITE_API_BASE_URL = 'https://your-api-domain.com'

// frontend/src/api/request.js
const request = axios.create({
    baseURL: import.meta.env.VITE_API_BASE_URL,
    timeout: 10000
})

后端也一样,用python-dotenv加载 .env 文件,永远不要把密钥写死在代码里

💣 再说三个容易翻车的点(都是真金白银换的教训)

⚠️ 第一个:路径拼接的斜杠

后端路由@app.get("/users"),前端请求/users/(多了一个斜杠),在nginx下可能301重定向,cookie丢了。

建议统一规则:路由末尾不加斜杠,前端请求也不加

⚠️ 第二个:请求/响应拦截器里的"循环引用"

有人在拦截器里用response.data.data取数据,但刷新token的接口又走了同一个拦截器,结果死循环。

解决方案:在白名单接口的meta里加一个标记跳过拦截器

⚠️ 第三个:FastAPI的异步陷阱

如果你用了async def,里面却调用同步的SQLAlchemy操作,会阻塞事件循环。

要么全用def,要么用asyncio.to_thread。很多人不知道,上了生产才发现接口越跑越慢。

🧠 进阶思考:拆分vs聚合,你的项目适合哪种?

是不是以为前后端分离就一定得两个仓库、两套部署?不一定 。小型项目或内部工具,可以把Vue构建后的dist文件夹放到FastAPI的static目录下,用同一个端口服务。这样省去跨域配置,部署也简单。

如果团队超过3个人,或者前后端需要独立迭代,强烈建议完全分离:后端一套API,前端单独部署到CDN或Nginx。这好比厨房和餐厅:小饭馆可以共用空间,但连锁餐厅必须分开,才能同时接单。


好了,今天就聊到这儿。上面这些配置和结构,都是我在项目里反复打磨过的,你拿过去改改就能跑起来

如果你在实战中也遇到过奇葩坑,或者对某个步骤有疑问,欢迎评论区吐槽。程序媛的时间很宝贵,但分享经验这事儿,我乐意。觉得有用的话,点个赞+关注,让更多人看到这份"避坑指南"。

💬 最后啰嗦一句:技术文章写得再好,不如你自己动手敲一遍。赶紧打开编辑器,从那个CORS配置开始吧!

相关推荐
Kapaseker2 小时前
Python 提速 — 惰性导入
python
落魄江湖行2 小时前
入门篇四:Nuxt4布局系统:让页面框架复用变得简单
前端·vue·nuxt4
杜子不疼.2 小时前
Python + Ollama 本地跑大模型:零成本打造私有 AI 助手
开发语言·c++·人工智能·python
李昊哲小课2 小时前
pip缓存配置
python·缓存·pip
belldeep2 小时前
python:介绍 UV 安装,如何使用 UV 安装配置 OpenHarness
windows·python·环境变量·uv
理想三旬2 小时前
Numpy 数据库
python·机器学习·numpy
波诺波2 小时前
p3项目-模拟 PID 控制器用来调节直流电机的转速
python·pid
路飞雪吖~2 小时前
【测试】接口测试---1个框架,5个模块
开发语言·python·测试工具
q_35488851532 小时前
计算机毕业设计:Python居民出行规律可视化分析系统 Django框架 可视化 数据分析 PyEcharts 交通 深度学习(建议收藏)✅
人工智能·python·数据分析·车载系统·django·汽车·课程设计