FastAPI与Vue 3实战:构建前后端分离通讯录管理系统

发布时间:2026/9/2 10:33:12
FastAPI与Vue 3实战:构建前后端分离通讯录管理系统 在实际的 Python 后端开发学习中很多人掌握了 Flask 或 Django 的基础但面对 FastAPI 这类现代异步框架时往往不知如何构建一个结构清晰、前后端分离的完整项目。一个通讯录管理系统看似简单却涵盖了用户认证、数据增删改查、前后端交互、项目分层等核心工程实践。本文将带你从零开始使用 FastAPI 作为后端 API 服务Vue 3 作为前端框架构建一个功能完整的通讯录管理系统。整个过程不仅适用于毕业设计更能作为理解现代 Web 开发技术栈的实战案例。你将学习到如何设计 RESTful API、如何处理跨域、如何进行 JWT 认证、以及如何组织一个可维护的三层架构代码。1. 理解项目架构与技术选型在动手编码之前需要明确整个项目的技术栈和架构设计。这决定了代码的组织方式、开发效率和未来的可维护性。1.1 为什么选择 FastAPI Vue 3FastAPI 是一个基于 Python 类型提示的现代 Web 框架以其高性能和自动生成交互式 API 文档而闻名。对于毕业设计或中小型项目它能显著减少样板代码并利用 Pydantic 进行高效的数据验证。Vue 3 则以其组合式 API、更好的 TypeScript 支持和更高的性能成为当前前端开发的主流选择之一。两者结合可以构建出响应迅速、体验良好的单页面应用。这个技术栈的核心优势在于开发效率FastAPI 的自动文档和类型验证Vue 3 的响应式系统和组件化都能提升开发速度。清晰分离前后端完全独立通过 JSON API 通信便于团队协作和独立部署。现代特性支持异步处理、TypeScript、组合式函数等现代开发模式。1.2 通讯录系统的核心模块与数据模型一个基础的通讯录管理系统通常包含以下模块用户认证模块用户注册、登录、JWT令牌管理。联系人管理模块联系人的增、删、改、查CRUD操作。数据模型定义用户和联系人的数据结构。我们需要设计两个核心的数据模型用户 (User)用于系统登录和权限管理。联系人 (Contact)存储通讯录信息。对应的数据库表结构设计如下以 SQLite 为例便于快速启动-- 用户表 CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE, hashed_password VARCHAR(255) NOT NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 联系人表 CREATE TABLE IF NOT EXISTS contacts ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100) NOT NULL, phone VARCHAR(20), email VARCHAR(100), address TEXT, company VARCHAR(100), notes TEXT, owner_id INTEGER NOT NULL, -- 关联用户ID实现数据隔离 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (owner_id) REFERENCES users (id) ON DELETE CASCADE );owner_id字段是关键它确保了每个用户只能操作自己创建的联系人这是多用户系统的基础。1.3 项目三层架构设计为了代码清晰和易于维护后端采用经典的三层架构数据访问层 (Repository/DAO)负责直接与数据库交互执行 SQL 操作。业务逻辑层 (Service)包含核心业务规则调用数据访问层处理业务逻辑。表现层 (API Router)接收 HTTP 请求调用业务逻辑层返回 HTTP 响应。前端 Vue 3 项目则采用典型的基于vue-router,pinia(状态管理) 和axios(HTTP 客户端) 的结构。2. 搭建后端 FastAPI 服务环境环境准备是项目成功的第一步版本对齐能避免很多依赖冲突问题。2.1 Python 环境与依赖管理首先确保你的 Python 版本在 3.7 及以上。推荐使用虚拟环境隔离项目依赖。# 创建项目目录并进入 mkdir contact-system-backend cd contact-system-backend # 创建虚拟环境 (Windows) python -m venv venv venv\Scripts\activate # 创建虚拟环境 (macOS/Linux) python3 -m venv venv source venv/bin/activate创建requirements.txt文件列出项目所需的核心依赖fastapi0.104.1 uvicorn[standard]0.24.0 sqlalchemy2.0.23 pydantic2.5.0 pydantic-settings2.1.0 python-jose[cryptography]3.3.0 passlib[bcrypt]1.7.4 python-multipart0.0.6使用 pip 安装依赖pip install -r requirements.txt关键依赖说明uvicorn: ASGI 服务器用于运行 FastAPI 应用。sqlalchemy: ORM 工具用于以 Python 对象的方式操作数据库。pydanticpydantic-settings: 数据验证和设置管理。python-jose: 用于生成和验证 JWT 令牌。passlib: 用于安全地哈希和验证密码。2.2 项目目录结构规划清晰的目录结构是良好项目的开端。建议按以下方式组织contact-system-backend/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 应用入口 │ ├── core/ # 核心配置 │ │ ├── __init__.py │ │ ├── config.py # 应用配置 (数据库URL, JWT密钥等) │ │ └── security.py # 安全相关 (密码哈希, JWT) │ ├── db/ # 数据库相关 │ │ ├── __init__.py │ │ ├── base.py # 数据库引擎和会话工厂 │ │ ├── base_class.py # ORM 基类 │ │ └── init_db.py # 初始化数据库表 │ ├── models/ # SQLAlchemy 数据模型 │ │ ├── __init__.py │ │ ├── user.py │ │ └── contact.py │ ├── schemas/ # Pydantic 模式 (请求/响应模型) │ │ ├── __init__.py │ │ ├── user.py │ │ └── contact.py │ ├── crud/ # 数据访问层 (Create, Read, Update, Delete) │ │ ├── __init__.py │ │ ├── user.py │ │ └── contact.py │ ├── api/ # API 路由 │ │ ├── __init__.py │ │ ├── deps.py # 依赖项 (如获取当前用户) │ │ ├── routes/ │ │ │ ├── __init__.py │ │ │ ├── auth.py # 认证相关路由 │ │ │ └── contacts.py # 联系人相关路由 │ └── services/ # 业务逻辑层 (可选简单项目可合并到crud) │ ├── __init__.py │ ├── auth.py │ └── contact.py ├── requirements.txt └── .env # 环境变量文件 (不要提交到git)2.3 核心配置与数据库连接首先在app/core/config.py中定义配置使用 Pydantic Settings 管理环境变量from pydantic_settings import BaseSettings from typing import Optional class Settings(BaseSettings): # 从 .env 文件或环境变量读取 PROJECT_NAME: str Contact System API VERSION: str 1.0.0 API_V1_STR: str /api/v1 # 数据库配置 DATABASE_URL: str sqlite:///./contacts.db # 默认使用SQLite # JWT 配置 SECRET_KEY: str your-secret-key-change-in-production # 必须修改 ALGORITHM: str HS256 ACCESS_TOKEN_EXPIRE_MINUTES: int 30 class Config: env_file .env case_sensitive True settings Settings()在.env文件中可以覆盖默认配置生产环境务必修改SECRET_KEYSECRET_KEYyour-super-secret-and-long-key-here DATABASE_URLsqlite:///./contacts.db接着在app/db/base.py中设置数据库引擎和会话from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from app.core.config import settings # 创建数据库引擎 engine create_engine( settings.DATABASE_URL, connect_args{check_same_thread: False} # SQLite专用参数 ) # 创建会话工厂 SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine) # 声明基类所有模型都将继承它 Base declarative_base() # 依赖项获取数据库会话 def get_db(): db SessionLocal() try: yield db finally: db.close()3. 实现后端核心功能配置完成后我们从数据模型开始自底向上构建后端服务。3.1 定义数据模型与 Pydantic 模式在app/models/user.py中定义用户 ORM 模型from sqlalchemy import Boolean, Column, Integer, String, DateTime from sqlalchemy.sql import func from app.db.base_class import Base class User(Base): __tablename__ users id Column(Integer, primary_keyTrue, indexTrue) username Column(String(50), uniqueTrue, indexTrue, nullableFalse) email Column(String(100), uniqueTrue, indexTrue, nullableTrue) hashed_password Column(String(255), nullableFalse) is_active Column(Boolean, defaultTrue) created_at Column(DateTime(timezoneTrue), server_defaultfunc.now())在app/schemas/user.py中定义与用户相关的 Pydantic 模式用于请求验证和响应序列化from pydantic import BaseModel, EmailStr from datetime import datetime from typing import Optional # 用于创建用户的请求体 class UserCreate(BaseModel): username: str email: Optional[EmailStr] None password: str # 用于更新用户的请求体 (部分更新) class UserUpdate(BaseModel): email: Optional[EmailStr] None password: Optional[str] None # 返回给前端的用户信息 (不包含密码) class UserInDB(BaseModel): id: int username: str email: Optional[str] None is_active: bool created_at: datetime class Config: from_attributes True # 允许从ORM对象转换 # 用于登录的请求体 class Token(BaseModel): access_token: str token_type: str class TokenData(BaseModel): username: Optional[str] None联系人模型 (app/models/contact.py) 和模式 (app/schemas/contact.py) 的定义逻辑类似需要包含name,phone,email,address等字段并在模式中体现与用户的关联。3.2 实现数据访问层 (CRUD)在app/crud/user.py中实现用户相关的数据库操作from sqlalchemy.orm import Session from app.models.user import User from app.schemas.user import UserCreate from app.core.security import get_password_hash, verify_password def get_user_by_username(db: Session, username: str): return db.query(User).filter(User.username username).first() def create_user(db: Session, user_in: UserCreate): hashed_password get_password_hash(user_in.password) db_user User( usernameuser_in.username, emailuser_in.email, hashed_passwordhashed_password, ) db.add(db_user) db.commit() db.refresh(db_user) return db_user def authenticate_user(db: Session, username: str, password: str): user get_user_by_username(db, username) if not user: return False if not verify_password(password, user.hashed_password): return False return user密码哈希和验证在app/core/security.py中实现from passlib.context import CryptContext from datetime import datetime, timedelta from jose import JWTError, jwt from app.core.config import settings pwd_context CryptContext(schemes[bcrypt], deprecatedauto) def verify_password(plain_password, hashed_password): return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password): return pwd_context.hash(password) def create_access_token(data: dict, expires_delta: timedelta None): to_encode data.copy() if expires_delta: expire datetime.utcnow() expires_delta else: expire datetime.utcnow() timedelta(minutessettings.ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({exp: expire}) encoded_jwt jwt.encode(to_encode, settings.SECRET_KEY, algorithmsettings.ALGORITHM) return encoded_jwt联系人的 CRUD 操作在app/crud/contact.py中实现核心是确保所有查询都通过owner_id过滤实现数据隔离。3.3 构建 API 路由与依赖注入首先在app/api/deps.py中创建一个依赖项用于从请求中获取当前认证用户from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from sqlalchemy.orm import Session from app.core.config import settings from app.crud.user import get_user_by_username from app.db.base import get_db from app.schemas.user import TokenData oauth2_scheme OAuth2PasswordBearer(tokenUrlf{settings.API_V1_STR}/auth/login) async def get_current_user( db: Session Depends(get_db), token: str Depends(oauth2_scheme) ): credentials_exception HTTPException( status_codestatus.HTTP_401_UNAUTHORIZED, detailCould not validate credentials, headers{WWW-Authenticate: Bearer}, ) try: payload jwt.decode(token, settings.SECRET_KEY, algorithms[settings.ALGORITHM]) username: str payload.get(sub) if username is None: raise credentials_exception token_data TokenData(usernameusername) except JWTError: raise credentials_exception user get_user_by_username(db, usernametoken_data.username) if user is None: raise credentials_exception return user接着创建认证路由app/api/routes/auth.pyfrom datetime import timedelta from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm from sqlalchemy.orm import Session from app.core.config import settings from app.core.security import create_access_token from app.crud.user import authenticate_user, create_user from app.db.base import get_db from app.schemas.user import Token, UserCreate, UserInDB router APIRouter() router.post(/register, response_modelUserInDB) def register(user_in: UserCreate, db: Session Depends(get_db)): # 检查用户名是否已存在 db_user get_user_by_username(db, usernameuser_in.username) if db_user: raise HTTPException(status_code400, detailUsername already registered) # 创建用户 user create_user(dbdb, user_inuser_in) return user router.post(/login, response_modelToken) def login( db: Session Depends(get_db), form_data: OAuth2PasswordRequestForm Depends() ): user authenticate_user(db, form_data.username, form_data.password) if not user: raise HTTPException( status_codestatus.HTTP_401_UNAUTHORIZED, detailIncorrect username or password, headers{WWW-Authenticate: Bearer}, ) access_token_expires timedelta(minutessettings.ACCESS_TOKEN_EXPIRE_MINUTES) access_token create_access_token( data{sub: user.username}, expires_deltaaccess_token_expires ) return {access_token: access_token, token_type: bearer}创建联系人路由app/api/routes/contacts.py所有操作都依赖get_current_userfrom typing import List from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.api.deps import get_current_user from app.crud.contact import ( create_contact_for_user, get_contacts_by_owner, get_contact_by_id_and_owner, update_contact_for_user, delete_contact_for_user, ) from app.db.base import get_db from app.models.user import User from app.schemas.contact import Contact, ContactCreate, ContactUpdate router APIRouter() router.get(/, response_modelList[Contact]) def read_contacts( skip: int 0, limit: int 100, db: Session Depends(get_db), current_user: User Depends(get_current_user), ): contacts get_contacts_by_owner(db, owner_idcurrent_user.id, skipskip, limitlimit) return contacts router.post(/, response_modelContact, status_codestatus.HTTP_201_CREATED) def create_contact( contact_in: ContactCreate, db: Session Depends(get_db), current_user: User Depends(get_current_user), ): return create_contact_for_user(dbdb, contactcontact_in, owner_idcurrent_user.id) router.put(/{contact_id}, response_modelContact) def update_contact( contact_id: int, contact_in: ContactUpdate, db: Session Depends(get_db), current_user: User Depends(get_current_user), ): contact get_contact_by_id_and_owner(db, contact_idcontact_id, owner_idcurrent_user.id) if contact is None: raise HTTPException(status_code404, detailContact not found) return update_contact_for_user(dbdb, db_contactcontact, contact_incontact_in) router.delete(/{contact_id}, status_codestatus.HTTP_204_NO_CONTENT) def delete_contact( contact_id: int, db: Session Depends(get_db), current_user: User Depends(get_current_user), ): contact get_contact_by_id_and_owner(db, contact_idcontact_id, owner_idcurrent_user.id) if contact is None: raise HTTPException(status_code404, detailContact not found) delete_contact_for_user(dbdb, db_contactcontact) return None3.4 应用入口与 CORS 配置最后在app/main.py中创建 FastAPI 应用实例并集成所有路由和中间件from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.api.routes import auth, contacts from app.core.config import settings from app.db.base import Base, engine # 创建数据库表 Base.metadata.create_all(bindengine) app FastAPI(titlesettings.PROJECT_NAME, versionsettings.VERSION) # 设置 CORS 中间件允许前端应用访问 app.add_middleware( CORSMiddleware, allow_origins[http://localhost:5173], # Vue 开发服务器默认端口 allow_credentialsTrue, allow_methods[*], allow_headers[*], ) # 注册 API 路由 app.include_router(auth.router, prefixf{settings.API_V1_STR}/auth, tags[auth]) app.include_router(contacts.router, prefixf{settings.API_V1_STR}/contacts, tags[contacts]) app.get(/) def read_root(): return {message: Welcome to Contact System API}4. 搭建前端 Vue 3 项目后端 API 准备就绪后我们开始构建前端用户界面。4.1 初始化 Vue 3 项目与依赖安装使用 Vite 快速创建 Vue 3 项目npm create vuelatest contact-system-frontend # 根据提示选择项目配置建议添加 Router 和 Pinia cd contact-system-frontend npm install安装额外的依赖主要是 UI 组件库和 HTTP 客户端npm install axios pinia # 如果使用 Element Plus 作为 UI 库 npm install element-plus element-plus/icons-vue4.2 配置 Axios 与 API 服务层在src目录下创建utils/request.js文件配置 Axios 实例自动携带 JWT Token 并处理响应import axios from axios import { ElMessage } from element-plus import router from /router // 创建 axios 实例 const service axios.create({ baseURL: http://localhost:8000/api/v1, // 后端 API 地址 timeout: 10000 }) // 请求拦截器 service.interceptors.request.use( config { // 从 localStorage 获取 token const token localStorage.getItem(access_token) if (token) { config.headers[Authorization] Bearer ${token} } return config }, error { console.error(Request Error:, error) return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response { const res response.data // 可以根据后端返回的 code 进行判断 return res }, error { console.error(Response Error:, error.response) if (error.response.status 401) { ElMessage.error(登录已过期请重新登录) localStorage.removeItem(access_token) router.push(/login) } else if (error.response.status 403) { ElMessage.error(没有权限访问) } else if (error.response.status 404) { ElMessage.error(请求的资源不存在) } else if (error.response.status 500) { ElMessage.error(服务器内部错误) } else { ElMessage.error(error.response.data?.detail || 请求失败) } return Promise.reject(error) } ) export default service创建src/api目录定义与后端交互的接口函数例如auth.jsimport request from /utils/request export function login(data) { return request({ url: /auth/login, method: post, data: new URLSearchParams({ username: data.username, password: data.password }), headers: { Content-Type: application/x-www-form-urlencoded } }) } export function register(data) { return request({ url: /auth/register, method: post, data }) }以及contact.jsimport request from /utils/request export function getContacts(params) { return request({ url: /contacts/, method: get, params }) } export function createContact(data) { return request({ url: /contacts/, method: post, data }) } export function updateContact(id, data) { return request({ url: /contacts/${id}, method: put, data }) } export function deleteContact(id) { return request({ url: /contacts/${id}, method: delete }) }4.3 使用 Pinia 管理用户状态创建src/stores/auth.js管理用户的登录状态和 Tokenimport { defineStore } from pinia import { login as userLogin, register as userRegister } from /api/auth import { getCurrentUser } from /api/user export const useAuthStore defineStore(auth, { state: () ({ user: null, token: localStorage.getItem(access_token) || }), getters: { isAuthenticated: (state) !!state.token }, actions: { async login(loginForm) { try { const response await userLogin(loginForm) this.token response.access_token localStorage.setItem(access_token, this.token) // 获取用户信息 await this.fetchUser() return response } catch (error) { throw error } }, async register(registerForm) { try { const response await userRegister(registerForm) return response } catch (error) { throw error } }, async fetchUser() { if (this.token) { try { this.user await getCurrentUser() } catch (error) { this.logout() } } }, logout() { this.user null this.token localStorage.removeItem(access_token) } } })4.4 实现核心页面组件创建登录页面src/views/LoginView.vuetemplate div classlogin-container el-card classlogin-card h2通讯录系统登录/h2 el-form :modelloginForm :rulesrules refloginFormRef el-form-item propusername el-input v-modelloginForm.username placeholder用户名 prefix-iconUser / /el-form-item el-form-item proppassword el-input v-modelloginForm.password typepassword placeholder密码 prefix-iconLock keyup.enterhandleLogin / /el-form-item el-form-item el-button typeprimary clickhandleLogin :loadingloading stylewidth:100%登录/el-button /el-form-item /el-form div classfooter span还没有账号/span el-link typeprimary click$router.push(/register)立即注册/el-link /div /el-card /div /template script setup import { ref, reactive } from vue import { useRouter } from vue-router import { ElMessage } from element-plus import { useAuthStore } from /stores/auth const router useRouter() const authStore useAuthStore() const loginFormRef ref() const loading ref(false) const loginForm reactive({ username: , password: }) const rules { username: [{ required: true, message: 请输入用户名, trigger: blur }], password: [{ required: true, message: 请输入密码, trigger: blur }] } const handleLogin async () { await loginFormRef.value.validate(async (valid) { if (valid) { loading.value true try { await authStore.login(loginForm) ElMessage.success(登录成功) router.push(/) } catch (error) { // 错误信息已在 request 拦截器中统一处理 } finally { loading.value false } } }) } /script style scoped .login-container { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f7fa; } .login-card { width: 400px; } .footer { margin-top: 20px; text-align: center; } /style创建联系人列表页面src/views/ContactListView.vue实现增删改查的表格交互。该页面会调用contact.js中定义的 API 函数并使用 ElTable、ElDialog 等组件构建界面。5. 运行、验证与接口测试前后端代码完成后需要启动服务并进行功能验证。5.1 启动后端服务在contact-system-backend目录下运行uvicorn app.main:app --reload --host 0.0.0.0 --port 8000--reload参数使得代码修改后自动重启便于开发。启动成功后访问http://localhost:8000/docs即可看到自动生成的交互式 API 文档Swagger UI你可以直接在这里测试注册、登录、联系人管理等接口。5.2 启动前端服务在contact-system-frontend目录下运行npm run devVite 默认会在http://localhost:5173启动开发服务器。打开浏览器访问此地址。5.3 功能验证流程注册新用户在前端注册页面填写信息提交后查看后端数据库users表是否新增记录密码应为哈希值。用户登录使用注册的账号密码登录检查浏览器开发者工具 Network 面板登录请求应返回access_token并存储在localStorage。创建联系人登录后在联系人管理页面添加新联系人。查看后端contacts表新增记录的owner_id应对应登录用户的 ID。查询联系人刷新页面或点击查询应只看到当前用户创建的联系人。更新与删除尝试修改或删除一条联系人确认操作成功且数据隔离有效不能操作他人的联系人。5.4 使用自动化测试验证接口为关键接口编写简单的 Pytest 测试脚本放在tests/目录下# tests/test_auth.py import pytest from fastapi.testclient import TestClient from app.main import app client TestClient(app) def test_register(): response client.post( /api/v1/auth/register, json{username: testuser, password: testpass123} ) assert response.status_code 200 data response.json() assert data[username] testuser assert hashed_password not in data def test_login(): response client.post( /api/v1/auth/login, data{username: testuser, password: testpass123} ) assert response.status_code 200 data response.json() assert access_token in data assert data[token_type] bearer运行测试pytest tests/6. 常见问题排查与解决方案在实际开发中你可能会遇到以下典型问题。6.1 跨域 (CORS) 错误现象前端控制台出现Access-Control-Allow-Origin相关的错误API 请求失败。原因浏览器出于安全考虑阻止了来自http://localhost:5173对http://localhost:8000的请求。解决确保后端app/main.py中正确配置了CORSMiddleware并且allow_origins列表包含了前端地址。生产环境需要设置为具体的域名。6.2 JWT 认证失败现象登录成功后操作联系人 API 返回401 Unauthorized。排查检查前端request.js中是否正确将 Token 设置到了Authorization请求头格式为Bearer token。检查后端get_current_user依赖项中的 Token 解码逻辑确认SECRET_KEY前后一致。检查 Token 是否已过期默认30分钟。解决在前端拦截器中捕获 401 错误自动跳转到登录页。6.3 数据库操作异常现象创建或查询数据时后端抛出 SQLAlchemy 相关异常。常见原因与解决问题现象可能原因检查与解决sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table数据库表未创建确认Base.metadata.create_all(bindengine)已执行。检查模型类是否正确定义并导入。sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed违反了唯一性约束如重复用户名在业务逻辑中先查询是否存在或在数据库层面处理异常返回友好提示。AttributeError: NoneType object has no attribute id查询结果为空None在调用对象属性前务必判断对象是否存在。使用get_contact_by_id_and_owner这类封装好的函数。6.4 前端路由守卫与权限控制需求未登录用户访问/首页时应重定向到登录页。实现在 Vue Router 的全局前置守卫中判断// router/index.js import { createRouter, createWebHistory } from vue-router import { useAuthStore } from /stores/auth const router createRouter({ history: createWebHistory(), routes: [/* ...你的路由定义 */] }) router.beforeEach((to, from, next) { const authStore useAuthStore() const isAuthenticated authStore.isAuthenticated // 假设需要认证的路由都有 meta: { requiresAuth: true } if (to.matched.some(record record.meta.requiresAuth)) { if (!isAuthenticated) { next(/login) } else { next() } } else { next() } })7. 生产环境部署与优化建议学习环境跑通后若想部署到生产环境需要考虑以下方面。7.1 后端部署配置调整更换数据库将 SQLite 更换为 PostgreSQL 或 MySQL。修改DATABASE_URL例如postgresql://user:passwordlocalhost/dbname。管理密钥SECRET_KEY必须使用强随机字符串并通过环境变量注入绝对不要写在代码中。使用生产级服务器使用uvicorn配合gunicornUnix或反向代理如 Nginx来运行 FastAPI。# 使用 gunicorn 启动多个 worker gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000启用 HTTPS通过 Nginx 配置 SSL 证书并将流量代理到后端应用。日志与监控配置结构化日志如使用loguru并集成监控工具如 Prometheus。7.2 前端构建与部署构建生产版本npm run build生成的dist目录包含静态文件。部署静态文件可以将dist目录的内容部署到 Nginx、Apache 或对象存储如 AWS S3、阿里云 OSS并配置 CDN。API 地址配置生产环境的前端需要连接生产环境的 API。可以通过环境变量或构建时注入的方式配置VITE_API_BASE_URL。7.3 安全与性能最佳实践密码安全始终使用passlib的bcrypt哈希密码切勿明文存储。SQL 注入防护使用 SQLAlchemy 的 ORM 或参数化查询避免手动拼接 SQL 字符串。输入验证充分利用 Pydantic 模型进行请求数据验证定义字段类型、长度、正则等规则。速率限制对登录等接口添加速率限制防止暴力破解。前端数据校验后端验证是必须的前端验证是为了更好的用户体验两者缺一不可。错误处理避免将详细的堆栈信息返回给客户端应记录到日志返回通用的错误信息。这个基于 FastAPI 和 Vue 3 的通讯录管理系统项目完整展示了现代全栈开发的核心流程。从设计数据模型、构建 RESTful API、实现 JWT 认证到前端状态管理、组件开发和路由守卫每一步都关联着实际开发中的关键决策。你可以在此基础上继续扩展例如加入联系人分组、导入导出、生日提醒等功能或者尝试集成更复杂的 UI 库、使用 TypeScript 强化类型将其作为一个扎实的起点深入探索更广阔的全栈开发世界。