保姆级教程!手把手教你搭建FastAPI + Vue3前后端分离项目

前言

随着现代Web应用的发展,前后端分离架构已经成为主流开发模式。这种架构不仅提高了开发效率,还增强了系统的可维护性和扩展性。本文将带你从零开始,使用当下最热门的技术栈------FastAPI(Python后端框架)和Vue3(前端框架),搭建一个完整的前后端分离项目。

选择FastAPI作为后端框架是因为它具备高性能、易用性强、自动生成API文档等优势;而Vue3作为前端框架则以其响应式系统、组件化开发和优秀的开发体验著称。通过本文的学习,你将掌握完整的全栈开发流程,并为后续深入学习打下坚实基础。

技术栈介绍

  • 后端技术栈
    • FastAPI:现代、快速(高性能)的Python Web框架
    • Python 3.8+
    • uvicorn:ASGI服务器用于运行FastAPI应用
  • 前端技术栈
    • Vue3:渐进式JavaScript框架
    • Vite:下一代前端构建工具
    • vue-router:Vue.js官方路由管理器
    • axios:基于Promise的HTTP客户端

我们将通过一个简单的任务管理系统示例,实现基本的增删改查(CRUD)功能,帮助你快速上手这套技术组合。

后端项目搭建

首先我们来创建FastAPI后端项目。创建一个新的目录并初始化项目:

注:默认是在Git Bash上操作的

创建目录并进入目录

bash 复制代码
# 创建目录
mkdir fastapi-vue3-demo
# 进入目录
cd fastapi-vue3-demo

创建虚拟环境

bash 复制代码
python -m venv venv

激活虚拟环境

bash 复制代码
# Windows
source venv\Scripts\activate
# Linux/Mac
source venv/bin/activate

安装fastapi依赖

bash 复制代码
pip install "fastapi[standard]"

创建后端主文件backend/main.py

python 复制代码
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uvicorn

app = FastAPI(title="Task Manager API", version="1.0.0")

# 数据模型定义
class Task(BaseModel):
    id: Optional[int] = None
    title: str
    description: Optional[str] = None
    completed: bool = False

# 模拟数据库(实际项目中应使用真实数据库)
tasks = [
    Task(id=1, title="学习FastAPI", description="掌握FastAPI基础知识", completed=True),
    Task(id=2, title="学习Vue3", description="了解Vue3 Composition API", completed=False)
]
next_id = 3

# API路由实现
@app.get("/")
async def root():
    return {"message": "欢迎使用Task Manager API"}

@app.get("/tasks", response_model=List[Task])
async def get_tasks():
    return tasks

@app.get("/tasks/{task_id}", response_model=Task)
async def get_task(task_id: int):
    for task in tasks:
        if task.id == task_id:
            return task
    raise HTTPException(status_code=404, detail="Task not found")

@app.post("/tasks", response_model=Task)
async def create_task(task: Task):
    global next_id
    task.id = next_id
    next_id += 1
    tasks.append(task)
    return task

@app.put("/tasks/{task_id}", response_model=Task)
async def update_task(task_id: int, updated_task: Task):
    for index, task in enumerate(tasks):
        if task.id == task_id:
            updated_task.id = task_id
            tasks[index] = updated_task
            return updated_task
    raise HTTPException(status_code=404, detail="Task not found")

@app.delete("/tasks/{task_id}")
async def delete_task(task_id: int):
    for index, task in enumerate(tasks):
        if task.id == task_id:
            tasks.pop(index)
            return {"message": "Task deleted successfully"}
    raise HTTPException(status_code=404, detail="Task not found")

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

启动后端服务:

bash 复制代码
uvicorn backend.main:app --reload

访问 http://localhost:8000/docs 查看自动生成的API文档。

前端项目搭建

接下来我们创建Vue3前端项目。确保已安装Node.js,然后执行:

创建前端项目

bash 复制代码
npm create vite@latest frontend --template vue

进入前端项目

bash 复制代码
cd frontend

安装默认依赖

bash 复制代码
npm install

安装vue-router和axios

bash 复制代码
npm install vue-router@4 axios

修改 frontend/src/main.js 添加路由支持:

javascript 复制代码
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import Home from './components/Home.vue'
import TaskList from './components/TaskList.vue'

const routes = [
  { path: '/', component: Home },
  { path: '/tasks', component: TaskList }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

createApp(App).use(router).mount('#app')

创建主页组件 frontend/src/components/Home.vue

vue 复制代码
<template>
  <div class="home">
    <h1>欢迎使用任务管理系统</h1>
    <p>这是一个使用FastAPI和Vue3构建的前后端分离应用</p>
    <router-link to="/tasks">查看任务列表</router-link>
  </div>
</template>
<script>
export default {
  name: 'Home'
}
</script>
<style scoped>
.home {
  text-align: center;
  margin-top: 50px;
}
a {
  display: inline-block;
  margin-top: 20px;
  padding: 10px 20px;
  background-color: #42b983;
  color: white;
  text-decoration: none;
  border-radius: 4px;
}
</style>

创建任务列表组件 frontend/src/components/TaskList.vue

vue 复制代码
<template>
  <div class="task-list">
    <h1>任务列表</h1>
    <div class="task-form">
      <input v-model="newTask.title" placeholder="任务标题" />
      <textarea v-model="newTask.description" placeholder="任务描述"></textarea>
      <button @click="addTask">添加任务</button>
    </div>
    
    <div v-if="loading">加载中...</div>
    <div v-else>
      <div v-for="task in tasks" :key="task.id" class="task-item">
        <h3>{{ task.title }} 
          <span :class="['status', {completed: task.completed}]">
            {{ task.completed ? '已完成' : '未完成' }}
          </span>
        </h3>
        <p>{{ task.description }}</p>
        <div class="actions">
          <button @click="toggleTask(task)" :class="{complete: !task.completed, undo: task.completed}">
            {{ task.completed ? '撤销' : '完成' }}
          </button>
          <button @click="deleteTask(task.id)" class="delete">删除</button>
        </div>
      </div>
    </div>
    
    <div class="back-link">
      <router-link to="/">返回首页</router-link>
    </div>
  </div>
</template>
<script>
import axios from 'axios'

const API_BASE_URL = 'http://localhost:8000'

export default {
  name: 'TaskList',
  data() {
    return {
      tasks: [],
      newTask: {
        title: '',
        description: ''
      },
      loading: true
    }
  },
  async mounted() {
    await this.fetchTasks()
  },
  methods: {
    async fetchTasks() {
      try {
        const response = await axios.get(`${API_BASE_URL}/tasks`)
        this.tasks = response.data
        this.loading = false
      } catch (error) {
        console.error('获取任务失败:', error)
        this.loading = false
      }
    },
    
    async addTask() {
      if (!this.newTask.title.trim()) return
      
      try {
        const response = await axios.post(`${API_BASE_URL}/tasks`, this.newTask)
        this.tasks.push(response.data)
        this.newTask = { title: '', description: '' }
      } catch (error) {
        console.error('添加任务失败:', error)
      }
    },
    
    async toggleTask(task) {
      try {
        const updatedTask = {
          ...task,
          completed: !task.completed
        }
        await axios.put(`${API_BASE_URL}/tasks/${task.id}`, updatedTask)
        task.completed = !task.completed
      } catch (error) {
        console.error('更新任务失败:', error)
      }
    },
    
    async deleteTask(id) {
      try {
        await axios.delete(`${API_BASE_URL}/tasks/${id}`)
        this.tasks = this.tasks.filter(task => task.id !== id)
      } catch (error) {
        console.error('删除任务失败:', error)
      }
    }
  }
}
</script>
<style scoped>
.task-list {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.task-form {
  margin-bottom: 30px;
  padding: 20px;
  background-color: #f5f5f5;
  border-radius: 4px;
}

.task-form input,
.task-form textarea {
  width: 100%;
  margin-bottom: 10px;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.task-form button {
  background-color: #42b983;
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
}

.task-item {
  border: 1px solid #ddd;
  padding: 15px;
  margin-bottom: 15px;
  border-radius: 4px;
}

.status {
  font-size: 0.8em;
  padding: 2px 6px;
  border-radius: 4px;
  background-color: #f0ad4e;
  color: white;
}

.status.completed {
  background-color: #5cb85c;
}

.actions {
  margin-top: 10px;
}

.actions button {
  margin-right: 10px;
  padding: 5px 10px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.complete {
  background-color: #5cb85c;
  color: white;
}

.undo {
  background-color: #f0ad4e;
  color: white;
}

.delete {
  background-color: #d9534f;
  color: white;
}

.back-link {
  margin-top: 20px;
}

.back-link a {
  color: #42b983;
  text-decoration: none;
}
</style>

更新 frontend/src/App.vue

vue 复制代码
<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link> |
      <router-link to="/tasks">任务管理</router-link>
    </nav>
    <router-view/>
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>

启动前端开发服务器:

bash 复制代码
cd frontend
npm run dev

解决跨域问题

由于前后端分别运行在不同端口(前端5173,后端8000),我们需要解决跨域问题。修改后端 backend/main.py 添加CORS支持:

python 复制代码
from fastapi.middleware.cors import CORSMiddleware

# 添加CORS中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],  # Vite默认端口
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

重新启动后端服务即可。

总结与展望

通过本文的学习,我们成功搭建了一个完整的FastAPI + Vue3前后端分离项目,实现了基本的CRUD功能。这个项目虽然简单,但它为你提供了以下几个重要知识点:

  1. FastAPI后端开发:包括路由定义、数据模型验证、异常处理等
  2. Vue3前端开发:使用Composition API、组件化开发、路由管理等
  3. 前后端交互:通过RESTful API进行数据交换
  4. 开发环境配置:项目结构规划、依赖管理等

这只是全栈开发的第一步,后续你可以在此基础上:

  • 集成数据库(如SQLite、PostgreSQL等)
  • 添加用户认证和权限管理
  • 实现更复杂的业务逻辑
  • 部署到生产环境
  • 添加单元测试和集成测试

如果你想深入了解这些技术栈的更多高级特性,欢迎关注我们的系列视频教程,我们将从基础到进阶,一步步带你成为全栈开发高手!


作者简介:专注于Python和前端技术分享,致力于帮助开发者快速掌握现代Web开发技能。

查看更多教程,小破站搜:mldong666

希望这篇文章对你有所帮助!如果有任何问题或建议,欢迎在评论区留言交流。

相关推荐
java水泥工2 小时前
学科竞赛管理系统|基于SpringBoot和Vue的学科竞赛管理系统(源码+数据库+文档)
数据库·vue.js·spring boot
千里码aicood2 小时前
python+vue智慧物业管理系统设计(源码+文档+调试+基础修改+答疑)
vue.js·spring boot·后端
乐~~~2 小时前
解决avue-input-tree组件重置数据不回显/重置失败
前端·javascript·vue.js
Q_Q5110082852 小时前
python+uniapp基于微信小程序美食点餐系统
spring boot·python·微信小程序·django·flask·uni-app·node.js
小关会打代码3 小时前
关于Pycharm中在运行出现语法错误:Non-UTF-8 code starting with
ide·python·pycharm
用户3721574261353 小时前
Python 高效将 PDF 转换为 HTML 的实用指南
python
深栈3 小时前
机器学习:编码方式
人工智能·python·机器学习·编码
yzx9910133 小时前
Django 搭配数据库开发智慧园区系统全攻略
python·django·数据库开发
PixelMind3 小时前
【LLIE技术专题】 SCI代码讲解
图像处理·python·低照度图像增强·llie