N

ninglab

记录代码,沉淀逻辑

初始化中...

© 2026 ninglab · 正在为你准备内容...

返回列表

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.