Python 全栈开发 2026:FastAPI + Vue3 + Docker 从零到部署完整指南
一、引言
2026年的Python全栈开发,核心技术栈已经明确:FastAPI(后端)+ Vue 3(前端)+ Docker(部署)。这套组合是企业中使用最广泛的Python全栈方案,兼顾了开发效率、运行性能和运维便利性。
本文将提供一份完整的从零到部署的全栈开发指南,包含项目结构、前后端联调、数据库设计和生产部署,全程可操作、可落地。
二、整体架构设计
2.1 技术栈选型
| 层级 | 技术选型 | 说明 |
|---|---|---|
| 前端框架 | Vue 3 + TypeScript | Composition API, 响应式系统 |
| 构建工具 | Vite | 极速开发体验 |
| UI组件库 | Element Plus | 成熟的企业级组件库 |
| 状态管理 | Pinia | Vue 3 官方推荐 |
| 后端框架 | FastAPI 2.0 | 原生异步、自动文档 |
| ORM | SQLAlchemy 2.0 | 异步支持 |
| 数据库 | PostgreSQL | 最先进的开源关系型数据库 |
| 缓存 | Redis | 高性能缓存 |
| 容器化 | Docker + Docker Compose | 一键部署 |
| 反向代理 | Nginx | 静态文件 + API代理 |
2.2 项目结构
fullstack-app/ ├── backend/ │ ├── app/ │ │ ├── main.py │ │ ├── config.py │ │ ├── dependencies.py │ │ ├── models/ │ │ ├── schemas/ │ │ ├── services/ │ │ ├── repositories/ │ │ └── routers/ │ ├── alembic/ │ ├── tests/ │ ├── Dockerfile │ └── requirements.txt ├── frontend/ │ ├── src/ │ │ ├── components/ │ │ ├── views/ │ │ ├── stores/ │ │ ├── api/ │ │ ├── router/ │ │ └── App.vue │ ├── Dockerfile │ ├── vite.config.ts │ └── package.json ├── nginx/ │ └── nginx.conf ├── docker-compose.yml └── .env三、后端开发
3.1 配置管理
# backend/app/config.pyfrompydantic_settingsimportBaseSettingsfromtypingimportListclassSettings(BaseSettings):PROJECT_NAME:str="Fullstack App"VERSION:str="1.0.0"ENVIRONMENT:str="development"DEBUG:bool=True# 数据库DATABASE_URL:str="postgresql+asyncpg://user:password@localhost:5432/app"REDIS_URL:str="redis://localhost:6379/0"# JWTJWT_SECRET_KEY:str="your-secret-key-change-in-production"JWT_ALGORITHM:str="HS256"JWT_EXPIRE_MINUTES:int=60# CORSCORS_ORIGINS:List[str]=["http://localhost:5173"]model_config={"env_file":".env"}settings=Settings()3.2 产品管理 API
# backend/app/routers/products.pyfromfastapiimportAPIRouter,HTTPException,status,QueryfromtypingimportList,Optionalfromapp.dependenciesimportDBSession,CurrentUser,Paginationfromapp.schemas.productimportProductCreate,ProductResponse,ProductUpdatefromapp.services.product_serviceimportProductService router=APIRouter()@router.post("/",response_model=ProductResponse,status_code=status.HTTP_201_CREATED)asyncdefcreate_product(data:ProductCreate,db:DBSession,user:CurrentUser):service=ProductService(db)returnawaitservice.create(data,user["id"])@router.get("/",response_model=dict)asyncdeflist_products(db:DBSession,pagination:Pagination,search:Optional[str]=Query(None),category:Optional[str]=Query(None),min_price:Optional[float]=Query(None),max_price:Optional[float]=Query(None),sort_by:Optional[str]=Query("created_at"),sort_order:Optional[str]=Query("desc"),):service=ProductService(db)items,total=awaitservice.list_with_filters(offset=pagination["offset"],limit=pagination["page_size"],search=search,category=category,min_price=min_price,max_price=max_price,sort_by=sort_by,sort_order=sort_order,)return{"items":items,"total":total,"page":pagination["page"],"page_size":pagination["page_size"],"total_pages":(total+pagination["page_size"]-1)//pagination["page_size"],}@router.get("/{product_id}",response_model=ProductResponse)asyncdefget_product(product_id:str,db:DBSession):service=ProductService(db)product=awaitservice.get_by_id(product_id)ifnotproduct:raiseHTTPException(status_code=404,detail="产品不存在")returnproduct@router.put("/{product_id}",response_model=ProductResponse)asyncdefupdate_product(product_id:str,data:ProductUpdate,db:DBSession,user:CurrentUser):service=ProductService(db)product=awaitservice.update(product_id,data,user["id"])ifnotproduct:raiseHTTPException(status_code=404,detail="产品不存在")returnproduct@router.delete("/{product_id}",status_code=status.HTTP_204_NO_CONTENT)asyncdefdelete_product(product_id:str,db:DBSession,user:CurrentUser):ifuser["role"]!="admin":raiseHTTPException(status_code=403,detail="权限不足")service=ProductService(db)success=awaitservice.delete(product_id)ifnotsuccess:raiseHTTPException(status_code=404,detail="产品不存在")四、前端开发
4.1 API 封装
// frontend/src/api/client.tsimportaxiosfrom'axios';import{useAuthStore}from'@/stores/auth';importrouterfrom'@/router';constapiClient=axios.create({baseURL:import.meta.env.VITE_API_BASE_URL||'/api',timeout:10000,headers:{'Content-Type':'application/json',},});// 请求拦截器apiClient.interceptors.request.use((config)=>{constauthStore=useAuthStore();if(authStore.token){config.headers.Authorization=`Bearer${authStore.token}`;}returnconfig;},(error)=>Promise.reject(error));// 响应拦截器apiClient.interceptors.response.use((response)=>response.data,(error)=>{if(error.response?.status===401){constauthStore=useAuthStore();authStore.logout();router.push('/login');}returnPromise.reject(error);});exportdefaultapiClient;4.2 产品管理 API
// frontend/src/api/products.tsimportapiClientfrom'./client';exportinterfaceProduct{id:string;name:string;description:string;price:number;category:string;stock:number;image_url:string;created_at:string;updated_at:string;}exportinterfaceProductListParams{page?:number;page_size?:number;search?:string;category?:string;min_price?:number;max_price?:number;sort_by?:string;sort_order?:string;}exportinterfaceProductListResponse{items:Product[];total:number;page:number;page_size:number;total_pages:number;}exportconstproductApi={list(params:ProductListParams={}){returnapiClient.get('/v1/products/',{params})asPromise<ProductListResponse>;},get(id:string){returnapiClient.get(`/v1/products/${id}`)asPromise<Product>;},create(data:Omit<Product,'id'|'created_at'|'updated_at'>){returnapiClient.post('/v1/products/',data)asPromise<Product>;},update(id:string,data:Partial<Product>){returnapiClient.put(`/v1/products/${id}`,data)asPromise<Product>;},delete(id:string){returnapiClient.delete(`/v1/products/${id}`);},};4.3 Pinia 状态管理
// frontend/src/stores/products.tsimport{defineStore}from'pinia';import{ref,computed}from'vue';import{productApi,Product,ProductListParams}from'@/api/products';exportconstuseProductStore=defineStore('products',()=>{constitems=ref<Product[]>([]);consttotal=ref(0);constloading=ref(false);consterror=ref<string|null>(null);constcurrentPage=ref(1);constpageSize=ref(10);constfilters=ref<ProductListParams>({});consttotalPages=computed(()=>Math.ceil(total.value/pageSize.value));asyncfunctionfetchProducts(params?:ProductListParams){loading.value=true;error.value=null;try{constmergedParams={page:currentPage.value,page_size:pageSize.value,...filters.value,...params,};constresponse=awaitproductApi.list(mergedParams);items.value=response.items;total.value=response.total;currentPage.value=response.page;if(params){filters.value={...filters.value,...params};}}catch(e:any){error.value=e.response?.data?.detail||'获取产品列表失败';}finally{loading.value=false;}}asyncfunctioncreateProduct(data:Omit<Product,'id'|'created_at'|'updated_at'>){constproduct=awaitproductApi.create(data);awaitfetchProducts();returnproduct;}asyncfunctionupdateProduct(id:string,data:Partial<Product>){constproduct=awaitproductApi.update(id,data);constindex=items.value.findIndex((item)=>item.id===id);if(index!==-1){items.value[index]=product;}returnproduct;}asyncfunctiondeleteProduct(id:string){awaitproductApi.delete(id);items.value=items.value.filter((item)=>item.id!==id);total.value--;}return{items,total,loading,error,currentPage,pageSize,filters,totalPages,fetchProducts,createProduct,updateProduct,deleteProduct,};});4.4 Vue 3 组合式组件
<!-- frontend/src/views/ProductList.vue --> <script setup lang="ts"> import { onMounted, ref } from 'vue'; import { useProductStore } from '@/stores/products'; import { ElMessage, ElMessageBox } from 'element-plus'; import type { Product } from '@/api/products'; import ProductForm from '@/components/ProductForm.vue'; const store = useProductStore(); const showForm = ref(false); const editingProduct = ref<Product | null>(null); const searchQuery = ref(''); onMounted(() => { store.fetchProducts(); }); function handleSearch() { store.fetchProducts({ search: searchQuery.value || undefined }); } function handlePageChange(page: number) { store.currentPage = page; store.fetchProducts(); } function handleCreate() { editingProduct.value = null; showForm.value = true; } function handleEdit(product: Product) { editingProduct.value = { ...product }; showForm.value = true; } async function handleDelete(product: Product) { try { await ElMessageBox.confirm( `确定要删除产品「${product.name}」吗?`, '确认删除', { type: 'warning' } ); await store.deleteProduct(product.id); ElMessage.success('删除成功'); } catch { // 取消操作 } } async function handleFormSubmit(data: any) { try { if (editingProduct.value) { await store.updateProduct(editingProduct.value.id, data); ElMessage.success('更新成功'); } else { await store.createProduct(data); ElMessage.success('创建成功'); } showForm.value = false; } catch (e: any) { ElMessage.error(e.response?.data?.detail || '操作失败'); } } </script> <template> <div class="product-list"> <div class="toolbar"> <el-input v-model="searchQuery" placeholder="搜索产品..." @keyup.enter="handleSearch" style="width: 300px" > <template #append> <el-button @click="handleSearch">搜索</el-button> </template> </el-input> <el-button type="primary" @click="handleCreate">新增产品</el-button> </div> <el-table :data="store.items" v-loading="store.loading" stripe> <el-table-column prop="name" label="产品名称" /> <el-table-column prop="category" label="分类" width="120" /> <el-table-column prop="price" label="价格" width="120"> <template #default="{ row }"> ¥{{ row.price.toFixed(2) }} </template> </el-table-column> <el-table-column prop="stock" label="库存" width="100" /> <el-table-column label="操作" width="200"> <template #default="{ row }"> <el-button size="small" @click="handleEdit(row)">编辑</el-button> <el-button size="small" type="danger" @click="handleDelete(row)"> 删除 </el-button> </template> </el-table-column> </el-table> <el-pagination v-model:current-page="store.currentPage" :page-size="store.pageSize" :total="store.total" layout="total, prev, pager, next" @current-change="handlePageChange" /> <ProductForm v-if="showForm" :product="editingProduct" @submit="handleFormSubmit" @cancel="showForm = false" /> </div> </template>五、Docker 部署
5.1 docker-compose.yml
version:'3.8'services:backend:build:./backendcontainer_name:fullstack-backendports:-"8000:8000"env_file:-.envdepends_on:postgres:condition:service_healthyredis:condition:service_startedrestart:unless-stoppednetworks:-app-networkfrontend:build:./frontendcontainer_name:fullstack-frontendports:-"3000:80"depends_on:-backendrestart:unless-stoppednetworks:-app-networkpostgres:image:postgres:16-alpinecontainer_name:fullstack-postgresenvironment:POSTGRES_USER:${POSTGRES_USER:-app}POSTGRES_PASSWORD:${POSTGRES_PASSWORD:-app_password}POSTGRES_DB:${POSTGRES_DB:-fullstack}volumes:-postgres_data:/var/lib/postgresql/datahealthcheck:test:["CMD-SHELL","pg_isready -U ${POSTGRES_USER:-app}"]interval:10stimeout:5sretries:5networks:-app-networkredis:image:redis:7-alpinecontainer_name:fullstack-redisvolumes:-redis_data:/datanetworks:-app-networknginx:image:nginx:alpinecontainer_name:fullstack-nginxports:-"80:80"volumes:-./nginx/nginx.conf:/etc/nginx/nginx.conf:rodepends_on:-backend-frontendrestart:unless-stoppednetworks:-app-networkvolumes:postgres_data:redis_data:networks:app-network:driver:bridge5.2 Nginx 配置
events { worker_connections 1024; } http { upstream backend { server backend:8000; } upstream frontend { server frontend:80; } server { listen 80; # 前端静态资源 location / { proxy_pass http://frontend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # API 代理 location /api/ { proxy_pass http://backend/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # WebSocket 支持 location /ws/ { proxy_pass http://backend/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; } } }六、总结
FastAPI + Vue 3 + Docker的全栈技术栈组合,在2026年已经成为Python全栈开发的事实标准。这套组合的优势在于:
- FastAPI:原生异步、自动文档、类型安全,开发效率极高
- Vue 3:Composition API、响应式系统、Pinia状态管理,前端开发体验极佳
- Docker:一键部署、环境隔离、易于扩展,运维成本极低
完整的全栈开发流程:先设计数据模型→编写后端API→定义Pydantic Schema→开发前端页面→前后端联调→Docker部署。每一步都有清晰的最佳实践,开发者可以专注于业务逻辑而非基础设施。
对于想要快速交付全栈项目的团队来说,这套技术栈组合是2026年最高效的选择。