首页 > 基础资料 博客日记

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

2026-04-07 09:00:02基础资料围观1

本篇文章分享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配置开始吧!


文章来源:https://www.cnblogs.com/ymtianyu/p/19828178
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云