FastAPI + Vue3:轻量级全栈项目实战
前后端目录规范与依赖管理
FastAPI + Vue3:轻量级全栈项目实战
适用场景:快速原型 / 内部工具 / 个人项目
技术栈:FastAPI + Vue3 + Pinia + Axios + Docker
1. 目录规范与依赖管理
project/
├── backend/ # Python 后端
│ ├── app/
│ │ ├── main.py
│ │ ├── routers/
│ │ ├── models/ # SQLAlchemy / Pydantic
│ │ └── core/ # Config, Security
│ └── requirements.txt
└── frontend/ # Vue3 前端
├── src/
│ ├── api/
│ ├── stores/ # Pinia
│ └── composables/
└── package.json
初始化
Bash
# Backend
python -m venv .venv && source .venv/bin/activate
pip install "fastapi[standard]" pydantic passlib[bcrypt] python-jose[cryptography]
# Frontend
npm create vite@latest frontend -- --template vue-ts
cd frontend && npm i axios pinia vue-router
2. FastAPI 路由、Pydantic 校验与 JWT
Python
# backend/app/core/security.py
from datetime import datetime, timedelta
from jose import jwt
SECRET_KEY = "your-secret"
ALGORITHM = "HS256"
def create_token(data: dict, expires_delta: timedelta = timedelta(hours=24)):
to_encode = data.copy()
to_encode.update({"exp": datetime.utcnow() + expires_delta})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
Python
# backend/app/routers/auth.py
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from app.core.security import create_token
router = APIRouter(prefix="/auth", tags=["auth"])
class LoginReq(BaseModel):
username: str
password: str
@router.post("/login")
def login(payload: LoginReq):
if payload.username != "admin" or payload.password != "123":
raise HTTPException(401, "Invalid credentials")
return {"access_token": create_token({"sub": payload.username})}
3. Vue3 请求封装与拦截器
TypeScript
// frontend/src/api/client.ts
import axios from "axios"
import { useAuthStore } from "@/stores/auth"
const api = axios.create({ baseURL: "/api", timeout: 5000 })
api.interceptors.request.use(cfg => {
const token = useAuthStore().token
if (token) cfg.headers.Authorization = `Bearer ${token}`
return cfg
})
api.interceptors.response.use(
res => res.data,
err => {
if (err.response?.status === 401) useAuthStore().logout()
return Promise.reject(err)
}
)
export default api
TypeScript
// frontend/src/stores/auth.ts
import { defineStore } from "pinia"
import { ref } from "vue"
export const useAuthStore = defineStore("auth", () => {
const token = ref<string | null>(localStorage.getItem("token"))
const setToken = (t: string) => { token.value = t; localStorage.setItem("token", t) }
const logout = () => { token.value = null; localStorage.removeItem("token") }
return { token, setToken, logout }
})
4. 本地开发与跨域配置
Vite 代理 (frontend/vite.config.ts)
TypeScript
export default defineConfig({
server: {
proxy: {
"/api": {
target: "http://127.0.0.1:8000",
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, "")
}
}
}
})
FastAPI CORS (backend/app/main.py)
Python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True,
allow_methods=["*"], allow_headers=["*"])
5. Docker Compose 编排与 Nginx
YAML
# docker-compose.yml
services:
backend:
build: ./backend
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
volumes: [./backend:/app]
frontend:
build: ./frontend
command: npm run dev -- --host
volumes: [./frontend:/app, /app/node_modules]
ports: ["5173:5173"]
nginx:
image: nginx:alpine
ports: ["80:80"]
volumes: ["./nginx.conf:/etc/nginx/conf.d/default.conf:ro"]
depends_on: [backend, frontend]
Nginx
# nginx.conf
server {
listen 80;
location / {
proxy_pass http://frontend:5173;
}
location /api/ {
proxy_pass http://backend:8000/api/;
proxy_set_header Host $host;
}
}
启动
Bash
docker compose up -d
# 访问 http://localhost
© 2026 ninglab. All rights reserved.