大模型项目:Vue3前端开发和前后端跨域连接
Vue 3 客户端开发 — 前后端分离项目实战
之前的课程完成了 FastAPI 后端服务器的搭建——邮件发送、验证码验证、MVC 分层架构。今天进入客户端(前端)开发领域——学习 Vue 3 框架,掌握脚手架搭建、路由配置、组件开发、Axios 前后端交互,将后端接口与前端页面打通,实现完整的前后端分离项目。
一、前后端分离架构概述
1. 什么是前后端分离?
在之前的课程中,我们的客户端页面(HTML)直接放在服务器的 templates 文件夹下,由 FastAPI 通过 Jinja2 模板引擎渲染——这是一体式项目模式。
前后端分离则是另一种架构:
┌─────────────────────┐ ┌─────────────────────┐
│ 前端 (Vue 3) │ HTTP │ 后端 (FastAPI) │
│ localhost:8080 │ ◄──────► │ localhost:8001 │
│ │ JSON │ │
│ • 页面展示 │ │ • 接口定义 (Controller) │
│ • 用户交互 │ │ • 业务逻辑 (Service) │
│ • 路由跳转 │ │ • 数据库操作 (Dao) │
│ • 发送 HTTP 请求 │ │ • LLM 调用 (AI) │
└─────────────────────┘ └─────────────────────┘
关键区别:
| 对比维度 | 一体式项目 | 前后端分离 |
|---|---|---|
| 前端运行方式 | 服务器渲染 HTML,浏览器直接访问服务器路径 | 独立 Vite 开发服务器,占用独立端口 |
| 前端端口 | 无(复用服务器端口 8001) | 8080(独立端口) |
| 后端端口 | 8001 | 8001 |
| 页面访问 | http://localhost:8001/users/goA | http://localhost:8080/testOne |
| 前后端通信 | 服务器内部跳转 | 前端通过 Axios 发 HTTP 请求调用后端接口 |
| 开发体验 | 改前端需刷新页面 | 热更新(HMR),保存即刷新 |
2. 技术选型
| 层级 | 技术 | 作用 |
|---|---|---|
| 核心框架 | Vue 3 | 构建用户界面的渐进式框架 |
| 构建工具 | Vite | 下一代前端构建工具,替代 Vue CLI |
| 路由 | Vue Router 4 | 管理客户端页面跳转(SPA 单页应用) |
| HTTP 请求 | Axios | 向后端发送请求、接收响应 |
| 包管理器 | npm | 安装和管理前端依赖 |
二、Vue 3 项目搭建
1. 环境准备
Vue 3 项目开发需要 Node.js 环境(v16.0 以上)。
node -v # 查看 Node.js 版本
npm -v # 查看 npm 版本
2. 使用 Vite 创建项目
# 切换磁盘路径到项目保存路径下
# 执行创建命令,my-vue-app 为项目名称(英文小写)
npm create vite@latest my-vue-app -- --template vue
# 进入项目目录
cd my-vue-app
# 安装依赖
npm install
3. 项目结构介绍
my-vue-app/
├── node_modules/ # 依赖包(npm install 后生成)
├── public/ # 静态资源(不经过编译,直接复制到输出目录)
├── components/ # 组件目录(页面级别的 .vue 文件)
├── src/ # 源代码目录
│ ├── assets/ # 需要编译的资源(图片、CSS 等)
│ ├── router/ # 路由配置目录
│ │ └── index.js # 路由定义文件
│ ├── App.vue # 根组件
│ ├── main.js # 入口文件(全局配置)
│ └── style.css # 全局样式
├── index.html # 入口 HTML 文件
├── package.json # 项目配置与依赖声明
└── vite.config.js # Vite 配置文件
核心文件说明:
index.html:浏览器访问的第一个页面,<div id="app">是 Vue 挂载点,<script type="module" src="/src/main.js">加载入口 JSmain.js:入口 JS 文件,创建 Vue 应用实例、注册路由、配置 Axios 等全局设置App.vue:根组件,所有页面的外层容器,<router-view/>负责切换显示不同路由下的页面vite.config.js:Vite 构建工具配置(端口、插件等)
三、项目配置
1. 配置启动端口与自动打开浏览器
在 vite.config.js 中添加 server 配置:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
// 开发服务器配置
server: {
host: 'localhost', // 服务器地址
port: 8080, // 启动端口(默认 5173,改为 8080)
open: true, // 启动后自动打开浏览器
},
})
关键点:
port: 8080把默认端口 5173 改为 8080,与后端 8001 区分open: true启动项目时自动用默认浏览器打开http://localhost:8080- 要注意项目网页启动后不用了要ctrl+c及时关掉,不然下一次启动后8080端口会因为网页还存在(手动叉掉网页也没用)被占,自动调去8081端口,导致前后端跨域链接出现问题。
host: 'localhost'限定只在本机访问
2. 启动项目
npm run dev
启动后终端显示:
VITE v5.x.x ready in xxx ms
➜ Local: http://localhost:8080/
热更新(HMR): 修改代码后按 Ctrl+S 保存,页面内容自动更新,无需手动刷新浏览器,大多数情况下不需要重启项目。
3. package.json — 依赖管理
{
"name": "my-vue-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite", // 启动开发服务器
"build": "vite build", // 构建生产版本
"preview": "vite preview" // 预览生产构建
},
"dependencies": {
"axios": "^1.19.0", // HTTP 请求库
"vue": "^3.4.0", // Vue 3 核心
"vue-router": "^4.6.4" // 路由管理
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0", // Vite 的 Vue 插件
"vite": "^5.0.0" // 构建工具
}
}
关键点:
dependencies:生产环境需要的包(axios、vue、vue-router)devDependencies:仅开发环境需要的包(vite、vite 插件)"type": "module":项目使用 ES Module 语法(import/export)- 安装新依赖:
npm install 包名→ 自动写入dependencies
四、客户端路由 — Vue Router【核心】
客户端路由的作用:控制如何通过 URL 路径访问不同的页面。与后端路由(FastAPI 的接口路径)不同,客户端路由管理的是页面跳转,不需要向服务器发请求。
1. 安装 Vue Router
npm install vue-router@4
2. 创建路由配置文件 src/router/index.js
// 引入路由相关函数
import { createRouter, createWebHistory } from "vue-router";
// 定义路由配置 --- 数组,每个页面是一个对象
const routes = [
{// 一个页面的访问路径就是一个js对象,至少包含2个属性:path、component
path: '/testOne', // 访问路径
component: () => import('../../components/TestOne.vue') // 懒加载组件
},
{
path: '/testTwo',
component: () => import('../../components/TestTwo.vue')
},
{
path: '/', // 根路径,作为默认首页
component: () => import('../../components/Login.vue')
}
]
// 创建路由实例(History 模式)
const router = createRouter({
history: createWebHistory(), // 干净的 URL 路径(无 # 号)
routes // 路由配置数组
})
// 导出路由实例
export default router
关键点:
| 概念 | 说明 |
|---|---|
createRouter() | Vue Router 4 的工厂函数,创建路由实例 |
createWebHistory() | HTML5 History 模式,URL 为干净的路径 /testOne,而非 Hash 模式的 /#/testOne。依赖浏览器 history.pushState() API |
path | 访问路径,和服务器的请求路径规则一致 |
component | 该路径对应的页面组件。() => import(...) 是懒加载写法——只有访问该路径时才加载组件,减少首屏加载时间 |
export default | 导出路由实例,供 main.js 注册 |
路由配置对象的核心属性:
| 属性 | 类型 | 说明 |
|---|---|---|
path | string | 页面访问路径,如 /testOne |
component | Component | 路径匹配时渲染的 Vue 组件 |
name | string(可选) | 路由名称,用于编程式导航 |
children | Array(可选) | 嵌套子路由 |
3. 在 main.js 中注册路由
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 导入路由
import router from './router'
app.use(router) // 注册路由到 Vue 应用
app.mount('#app')
关键点:
app.use(router)将路由实例注册到 Vue 应用中,如果不注册,路由不生效- 注册后,Vue 应用才能识别
<router-view/>和<router-link>组件
4. 修改 App.vue — 路由占位
<template>
<div>
<!-- router-view 是路由切换的占位标签,匹配到的页面组件会渲染在这里 -->
<router-view/>
</div>
</template>
<script setup>
</script>
<style>
</style>
关键点:
<router-view/>是 Vue Router 提供的内置组件,相当于一个"插槽"- 当访问
/testOne时,TestOne.vue的内容会渲染在<router-view/>的位置 - 任何时候,只要创建了新页面组件,先去配置路由——这是一个标准操作流程
5. 路由访问流程
用户在浏览器输入 http://localhost:8080/testOne
│
▼
Vue Router 拦截 URL,匹配 routes 数组
│
▼
找到 path: '/testOne' → 加载 TestOne.vue 组件
│
▼
TestOne.vue 的内容渲染到 App.vue 中的 <router-view/> 位置
│
▼
用户看到 TestOne 页面
五、Vue 单文件组件(.vue)
一个 .vue 文件就是一个组件(页面),由三部分组成:
<template>
<!-- HTML 模板 —— 页面的结构和内容 -->
</template>
<script setup>
<!-- JavaScript 逻辑 —— 响应式数据、函数、生命周期等 -->
</script>
<style scoped>
<!-- CSS 样式 —— scoped 表示样式只作用于当前组件 -->
</style>
三部分详解:
| 部分 | 作用 | 说明 |
|---|---|---|
<template> | HTML 模板 | 编写页面结构。必须有一个根元素包裹所有内容 |
<script setup> | JS 逻辑 | Vue 3 的语法糖,顶层绑定(变量、函数)自动暴露给模板,不需要 return |
<style scoped> | CSS 样式 | scoped 属性让样式只作用于当前组件,不会污染其他组件 |
项目中的实际用法:在本次项目中,直接把
.vue当作一个.html页面来写,不深入研究组件嵌套、父子传参等高级用法。
六、响应式数据 — ref
ref() 是 Vue 3 定义响应式数据的核心 API。数据变化时,页面自动更新。
TestOne.vue 实战
<template>
<div>
<h1>操作1 — v-bind</h1>
<form>
账号:<input type="text" v-bind:disabled="isDisabled">
<br>
密码:<input type="password">
<br>
<button type="button" @click="ChangeInput">点我1</button>
</form>
<h1>操作2 — v-show</h1>
<div v-show="isShow" style="border:1px solid red; width: 100%;height: 30px">
我显示了1
</div>
<div v-show="!isShow" style="border:1px solid red; width: 100%;height: 30px">
我显示了2
</div>
<button type="button" @click="isShow = !isShow">点我2</button>
</div>
</template>
<script setup>
// 导入响应式数据 API
import { ref } from "vue";
// ref() 定义响应式变量,初始值为 true
let isDisabled = ref(true);
function ChangeInput() {
// JS 中通过 .value 访问和修改
isDisabled.value = !isDisabled.value;
}
let isShow = ref(true);
</script>
关键点分析:
| 概念 | 代码 | 说明 |
|---|---|---|
| 定义响应式数据 | let isDisabled = ref(true) | 创建一个响应式引用,初始值为 true |
| 模板中使用 | :disabled="isDisabled" | 模板中自动解包,直接用变量名,不需要 .value |
| JS 中修改 | isDisabled.value = !isDisabled.value | JS 中必须用 .value 读取和修改 |
| 绑定属性 | v-bind:disabled="isDisabled" | 动态绑定 HTML 属性,简写 :disabled |
| 绑定事件 | @click="ChangeInput" | @ 是 v-on: 的简写,绑定点击事件 |
| 条件显示 | v-show="isShow" | 控制元素的显示/隐藏(切换 display:none) |
v-bind 简写规则:
<!-- 完整写法 -->
<input v-bind:disabled="isDisabled" />
<!-- 简写(推荐) -->
<input :disabled="isDisabled" />
v-on 简写规则:
<!-- 完整写法 -->
<button v-on:click="ChangeInput">按钮</button>
<!-- 简写(推荐) -->
<button @click="ChangeInput">按钮</button>
七、表单双向绑定 — v-model
v-model 实现表单元素和数据之间的双向绑定:输入框内容变化 → 数据自动更新;数据变化 → 输入框内容自动更新。
Login.vue 中的 v-model 实战
<template>
<div>
<form>
邮箱号:<input type="text" :disabled="!isCode" v-model="email">
<br>
验证码:<input type="text" :disabled="isCode" v-model="code">
<br>
<button type="button" @click="sendEmail" v-show="isCode">发送验证码</button>
<button type="button" @click="checkCode" v-show="!isCode">验证验证码</button>
</form>
</div>
</template>
<script setup>
import { ref, getCurrentInstance } from "vue";
const { proxy } = getCurrentInstance();
// 控制输入框是否可用
let isCode = ref(true);
// 双向绑定的数据
let email = ref("12345678@qq.com"); // 邮箱号
let code = ref(""); // 验证码
</script>
这个页面中的指令汇总:
| 指令 | 代码示例 | 作用 |
|---|---|---|
v-model | v-model="email" | 双向绑定:输入框 ↔ 变量,一方变另一方自动同步 |
:disabled | :disabled="!isCode" | 动态控制输入框是否可编辑 |
v-show | v-show="isCode" | 条件显示按钮(切换 display 属性) |
@click | @click="sendEmail" | 绑定点击事件 |
v-show vs v-if:
| 指令 | 原理 | 适用场景 |
|---|---|---|
v-show | 始终渲染 DOM,切换 display:none | 频繁切换显示/隐藏(本项目中使用) |
v-if | 条件为假时销毁 DOM,为真时重建 DOM | 切换不频繁,需要节省初始渲染开销 |
八、Axios — 前端与后端交互【核心】
客户端通过 Axios 向后端发送 HTTP 请求,获取服务器返回的数据。这是前后端分离中最关键的环节。
1. 安装 Axios
npm install axios
2. 全局配置 — main.js
在 main.js 中配置 Axios 的全局默认值,避免每个组件重复写配置信息:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 注册路由
import router from './router'
app.use(router)
// ========== Axios 全局配置 ==========
import axios from 'axios'
// ① 设置基础 URL —— 所有请求自动拼接此前缀
axios.defaults.baseURL = 'http://localhost:8001/'
// ② 设置 POST/PUT 请求的 Content-Type 为 JSON
axios.defaults.headers.post['Content-Type'] = 'application/json'
axios.defaults.headers.put['Content-Type'] = 'application/json'
// ③ 挂载到 Vue 全局属性 —— 组件中用 proxy.$axios 替代原生 axios
app.config.globalProperties.$axios = axios
app.mount('#app')
配置项详解:
| 配置项 | 值 | 作用 |
|---|---|---|
baseURL | 'http://localhost:8001/' | 后端服务器的公共路径前缀。之后发请求只需写相对路径如 users/sendEmail,实际请求 http://localhost:8001/users/sendEmail |
headers.post['Content-Type'] | 'application/json' | POST 请求时自动带上 JSON 请求头 |
globalProperties.$axios | axios | 挂载后所有组件通过 proxy.$axios 使用,不需要每个组件都 import axios |
3. 在组件中使用 Axios
获取 proxy 对象:
import { ref, getCurrentInstance } from "vue";
// getCurrentInstance() 获取当前组件实例
// proxy 是 Vue 的代理对象,挂载了 main.js 中 globalProperties 的所有属性
const { proxy } = getCurrentInstance();
之后用 proxy.$axios({...}) 发送请求:
proxy.$axios({
url: 'users/sendEmail', // 请求地址(自动拼接 baseURL)
method: 'get', // 请求方式
params: { // GET 请求的参数 → 拼接为 URL 查询字符串
email: sendEmail,
},
}).then(res => {
// 请求成功:res.data 是后端返回的 JSON 数据
console.log(res.data);
}).catch(err => {
// 请求失败:网络错误、后端未启动、跨域等
console.error(err);
});
4. GET 请求 vs POST 请求 — 参数传递对比
| GET 请求 | POST 请求 | |
|---|---|---|
| Axios 参数写法 | params: { email: 'xxx' } | data: { email: 'xxx', code: '1234' } |
| 参数位置 | 拼接在 URL 后面:?email=xxx | 放在请求体(Body)中 |
| 后端接收方式 | 形参 email: str | Pydantic 数据类 |
| 本项目用例 | 发送验证码 | 验证验证码 |
GET 请求示例(发送验证码):
function sendEmail() {
let sendEmail = email.value;
proxy.$axios({
url: 'users/sendEmail',
method: 'get',
params: { // ← GET 用 params
email: sendEmail, // key 必须和后端形参名一致!
},
}).then(res => {
let code = res.data.code;
let msg = res.data.msg;
if (code === 200) {
alert(msg || "验证码已发送,请查收邮箱");
} else {
alert(msg || "发送失败");
}
}).catch(err => {
alert("请求失败,请确认后端服务是否已启动:" + err.message);
});
}
POST 请求示例(验证验证码):
function checkCode() {
let checkCode = {
email: email.value, // key 必须和后端 BaseModel 的属性名一致
code: code.value,
};
proxy.$axios({
url: 'users/verifyEmail',
method: 'post',
data: checkCode, // ← POST 用 data
}).then(res => {
let code = res.data.code;
let msg = res.data.msg;
if (code === 200) {
alert(msg || "验证成功!");
} else {
alert(msg || "验证失败");
}
}).catch(err => {
alert("请求失败:" + err.message);
});
}
关键原则:
客户端发送参数时的 key 必须和后端接口形参名 / BaseModel 属性名一致。这是前后端交互的铁律——客户端和服务器通过 key:value 交互数据,只能通过 key 找 value。
5. 统一返回格式
后端所有接口返回统一的 JSON 格式:
{
"code": 200, // 状态码:200 成功,500 失败
"msg": "发送成功", // 提示信息
"data": null // 数据内容
}
前端根据 res.data.code 判断请求结果并展示对应的 msg 给用户。
九、Login.vue 完整流程解析
Login.vue 是前后端交互的实战综合案例,涉及双向绑定、条件渲染、事件绑定、Axios GET/POST 请求等核心知识点。
1. 完整代码
<template>
<div>
<form>
邮箱号:<input type="text" :disabled="!isCode" v-model="email">
<br>
验证码:<input type="text" :disabled="isCode" v-model="code">
<br>
<button type="button" @click="sendEmail" v-show="isCode">发送验证码</button>
<button type="button" @click="checkCode" v-show="!isCode">验证验证码</button>
</form>
</div>
</template>
<script setup>
import { ref, getCurrentInstance } from "vue";
const { proxy } = getCurrentInstance();
// 控制 UI 状态
let isCode = ref(true); // true → 可以发验证码,false → 可以验证
// 双向绑定的数据
let email = ref("12345678@qq.com");
let code = ref("");
// ========== 发送验证码 ==========
function sendEmail() {
let sendEmail = email.value;
proxy.$axios({
url: 'users/sendEmail',
method: 'get',
params: { email: sendEmail },
}).then(res => {
let code = res.data.code;
let msg = res.data.msg;
if (code === 200) {
isCode.value = !isCode.value; // 切换到验证码输入状态
alert(msg || "验证码已发送,请查收邮箱");
} else {
alert(msg || "发送失败,请稍后重试");
}
}).catch(err => {
alert("请求失败,请确认后端服务是否已启动:" + err.message);
});
}
// ========== 验证验证码 ==========
function checkCode() {
let checkCode = {
email: email.value,
code: code.value,
};
proxy.$axios({
url: 'users/verifyEmail',
method: 'post',
data: checkCode,
}).then(res => {
let code = res.data.code;
let msg = res.data.msg;
if (code === 200) {
alert(msg || "验证成功!");
isCode.value = true; // 恢复初始状态
} else {
alert(msg || "验证失败");
}
}).catch(err => {
alert("请求失败:" + err.message);
});
}
</script>
2. 交互状态机
Login.vue 的 UI 有两套状态,通过 isCode 变量切换:
初始状态 (isCode = true)
├── 邮箱输入框:可编辑(:disabled="!isCode" → :disabled="false")
├── 验证码输入框:禁用(:disabled="isCode" → :disabled="true")
├── "发送验证码"按钮:显示(v-show="isCode")
└── "验证验证码"按钮:隐藏(v-show="!isCode")
│
│ 点击"发送验证码" → 后端返回 code:200
│
▼
验证状态 (isCode = false)
├── 邮箱输入框:禁用
├── 验证码输入框:可编辑
├── "发送验证码"按钮:隐藏
└── "验证验证码"按钮:显示
│
│ 点击"验证验证码" → 后端返回 code:200
│
▼
回到初始状态 (isCode = true)
3. 数据流全景
【用户操作】输入邮箱号 → 点击"发送验证码"
│
▼
【前端 Login.vue】
sendEmail() 函数
├── email.value 获取用户输入的邮箱号
├── proxy.$axios → GET http://localhost:8001/users/sendEmail?email=xxx
└── .then(res) → 根据 res.data.code 判断结果
│
▼ HTTP 请求 ──────────────────────────
│
▼
【后端 stu_fastapi_te】
UsersController.send_email(email)
│
▼
UsersService.send_email(email)
├── UsersDao.check_email(email) → MySQL 查询邮箱是否注册
├── random 生成 4 位验证码
├── smtplib 通过 QQ SMTP 发送邮件
└── Redis 存储验证码(email → code,60 秒过期)
│
▼ 返回 JSON ──────────────────────────
│
▼
【前端 Login.vue】
.then(res) 收到响应
├── code === 200 → isCode 切换为 false → 显示验证码输入框
└── code !== 200 → alert 显示错误信息
══════════════════════════════════════════════
【用户操作】输入验证码 → 点击"验证验证码"
│
▼
【前端 Login.vue】
checkCode() 函数
├── 封装 { email, code } 为 JS 对象
├── proxy.$axios → POST http://localhost:8001/users/verifyEmail
│ Body: {"email":"xxx","code":"1234"}
└── .then(res) → 根据 res.data.code 判断
│
▼ HTTP 请求 ──────────────────────────
│
▼
【后端 stu_fastapi_te】
UsersController.verify_email(request)
│
▼
UsersService.verify_email(email, code)
├── Redis 读取该 email 对应的验证码
├── 比对用户输入的 code 与 Redis 中的 stored_code
└── 验证成功 → 删除 Redis 中的验证码(一次性使用)
│
▼ 返回 JSON ──────────────────────────
│
▼
【前端 Login.vue】
.then(res) 收到响应
├── code === 200 → 验证成功,isCode 恢复为 true
└── code !== 200 → alert 显示错误信息
4. 点击按钮后 .then(res => { 里面发生了什么?
.then(res => {
// res 是 axios 的响应对象,有6个常用属性
// res.status HTTP 状态码(200、201、500 等)
// res.statusText 状态文本("OK","Created"等)
// res.headers 响应头
// res.config 本次请求的配置
// res.request 发送请求的 XMLHttpRequest 对象
// res.data 后端接口返回的 JSON 数据(最常用)
// 实际代码中,只从 res.data 获取业务数据:
let code = res.data.code; // 业务状态码(200 成功 / 500 失败)
let msg = res.data.msg; // 提示信息(如 "发送成功" / "邮箱号不存在")
// 判定业务结果并更新前端状态
if (code === 200) {
// 成功 — 切换界面状态、弹出成功提示
isCode.value = !isCode.value;
alert(msg || "默认提示语");
} else {
// 失败 — 弹出后端的错误提示
alert(msg || "默认错误提示");
}
}).catch(err => {
// catch 捕获的不是业务错误,而是网络/服务器异常:
// — 后端服务没启动(ERR_CONNECTION_REFUSED)
// — 跨域被浏览器拦截(CORS error)
// — 网络断开、DNS 解析失败等
alert("请求失败,请确认后端服务是否已启动:" + err.message);
})
关键点:
res.data是后端接口return {...}返回的字典(自动 JSON 序列化).then()处理 HTTP 请求成功的情况(HTTP 200),但业务可能失败(code: 500),需要进一步判断.catch()处理 HTTP 请求本身失败的情况(网络错误、跨域、服务器未启动等)
十、跨域配置(CORS)— 后端配合
1. 为什么需要跨域配置?
同源策略要求 A 服务访问 B 服务必须满足"三同":
- 协议相同(http / https)
- IP 相同(localhost / 127.0.0.1)
- 端口相同(8080 / 8001)
前后端分离项目中,前端 localhost:8080 访问后端 localhost:8001,端口不同,违背同源策略。浏览器会拦截请求,抛出 CORS 错误。
2. 解决方案 — 后端配置 CORS
在 main.py 中添加跨域中间件(只需后端配置,前端无需任何处理):
# main.py
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:8080"], # 允许的前端源地址
allow_credentials=True, # 允许携带 Cookie
allow_methods=["*"], # 允许所有 HTTP 方法(GET/POST/PUT/DELETE)
allow_headers=["*"], # 允许所有请求头
)
关键点:
allow_origins指定哪些前端地址可以访问后端,生产环境应写具体域名allow_methods=["*"]允许所有请求方法- CORS 配置只在后端做,前端代码无需改动
- 配置后浏览器不再拦截前端到后端的 HTTP 请求
十一、完整开发流程总结
1. 前端开发标准步骤
① 创建项目
npm create vite@latest 项目名 -- --template vue
cd 项目名
npm install
② 安装依赖
npm install vue-router@4 # 路由
npm install axios # HTTP 请求
③ 配置 vite.config.js
server: { host, port: 8080, open: true }
④ 创建页面组件 (.vue)
components/xxx.vue
⑤ 配置路由 (src/router/index.js)
{ path: '/xxx', component: () => import('...') }
⑥ 在 main.js 注册路由
app.use(router)
⑦ 在 main.js 配置 Axios
axios.defaults.baseURL = 'http://localhost:8001/'
app.config.globalProperties.$axios = axios
⑧ 修改 App.vue
<router-view/>
⑨ 开发页面逻辑
导入 ref → 定义响应式数据 → 编写交互函数 → 使用 proxy.$axios 请求后端
⑩ 启动项目
npm run dev
2. 前后端端口与地址对照
| 前端 (Vue 3) | 后端 (FastAPI) | |
|---|---|---|
| 端口 | 8080 | 8001 |
| 访问地址 | http://localhost:8080 | http://localhost:8001 |
| 接口测试 | 通过 Axios 调用 | Swagger UI:http://localhost:8001/docs |
| 启动命令 | npm run dev | python main.py |
3. 前后端请求对应关系
| 功能 | 前端组件 | Axios 调用 | 后端接口 | 后端 Controller |
|---|---|---|---|---|
| 发送验证码 | Login.vue → sendEmail() | GET users/sendEmail?email=xxx | @users_router.get("/sendEmail") | UsersController.send_email |
| 验证验证码 | Login.vue → checkCode() | POST users/verifyEmail + JSON body | @users_router.post("/verifyEmail") | UsersController.verify_email |
4. 核心概念速查表
Vue 3 模板语法:
| 语法 | 完整写法 | 作用 |
|---|---|---|
:attr="val" | v-bind:attr="val" | 动态绑定 HTML 属性 |
@event="fn" | v-on:event="fn" | 绑定事件处理函数 |
v-model="val" | — | 表单双向绑定 |
v-show="cond" | — | 条件显示(display:none) |
{{ val }} | — | 文本插值 |
ref() vs reactive():
| 特性 | ref | reactive |
|---|---|---|
| 适用类型 | 基本类型、对象 | 仅对象/数组 |
| JS 中访问 | .value | 直接访问 |
| 模板中使用 | 直接使用 | 直接使用 |
| 本项目推荐 | ✅ 占 90%+ 场景 | 特定场景 |
Axios 请求参数:
| 请求方式 | 参数写法 | 参数位置 | 后端接收 |
|---|---|---|---|
| GET | params: { key: val } | URL 查询字符串 | 接口形参 |
| POST | data: { key: val } | 请求体 (JSON) | BaseModel 数据类 |
十二、课后实践
📝 任务要求
- 创建前端项目:使用 Vite 创建一个新的 Vue 3 项目
- 安装依赖:安装
vue-router和axios - 配置端口:在
vite.config.js中设置端口为 8080,开启自动打开浏览器 - 创建页面:创建至少 2 个
.vue页面组件 - 配置路由:在
src/router/index.js中配置路由,实现页面间跳转 - 配置 Axios:在
main.js中全局配置 Axios(baseURL、全局挂载) - 实现交互:参照 Login.vue 的模式,实现一个前后端交互功能(如注册、查询等)
- 启动后端:确保 FastAPI 服务器在 8001 端口运行,并已配置 CORS
🔧 准备工作检查清单
- Node.js 已安装(v16.0 以上),
node -v验证 - Vue 3 前端项目已创建,依赖已安装
- 前端端口 8080,后端端口 8001
- 后端 FastAPI 已配置 CORS(
allow_origins=["http://localhost:8080"]) - 前后端同时启动,浏览器控制台无 CORS 报错
- 了解
res.data的结构(code、msg、data)
⚠️ 常见问题
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 前端页面空白 | 路由未配置或 <router-view/> 未写 | 检查 router/index.js 和 App.vue |
| 请求报 CORS 错误 | 后端未配置跨域 | 在 main.py 添加 CORSMiddleware |
proxy.$axios 报 undefined | main.js 中未挂载 | 添加 app.config.globalProperties.$axios = axios |
| 后端接收不到参数 | 前端 key 和后端形参名不一致 | 确保 params/data 中的 key 与后端一致 |
npm run dev 报错 | 依赖未安装 | 执行 npm install |
.catch 触发但后端正常 | 跨域配置的 origin 不匹配 | 确认 allow_origins 中的端口号与前端一致 |
核心要点
- 前后端分离:前端 8080 端口(Vite) + 后端 8001 端口(FastAPI),通过 HTTP 通信,各自独立开发部署
- Vue SFC 三件套:
<template>(HTML)、<script setup>(JS 逻辑)、<style scoped>(CSS 样式) - 客户端路由:Vue Router 管理页面跳转,
<router-view/>是切换占位符,访问不同路径显示不同组件 - 响应式数据
ref():数据变化 → 页面自动更新;JS 中用.value,模板中直接使用 - 模板指令:
:绑定属性、@绑定事件、v-model双向绑定、v-show条件显示 - Axios 三部曲:①
main.js全局配置(baseURL + 挂载) → ② 组件中用getCurrentInstance().proxy.$axios获取 → ③ 发请求(GET 用params,POST 用data) - 前端交互铁律:前端参数 key 必须和后端形参名 / BaseModel 属性名完全一致
- CORS:同源策略限制 → 后端添加
CORSMiddleware中间件解决,前端无需处理 - 统一返回格式:
{"code": 状态码, "msg": 提示信息, "data": 数据},前端根据code判断业务结果
最终思考:从 day5 的 FastAPI 接口开发,到 day6 的邮件验证码后端实战,再到今天的 Vue 3 客户端开发——完成了从前到后的全栈项目闭环。Login.vue 的完整流程(用户输入 → Axios GET 发邮件 → 后端 Service/Dao/Redis 处理 → 返回 JSON → 前端 .then() 判断 → POST 验证 → 比对 Redis → 返回结果)就是前后端分离开发的标准范式。之后任何功能的开发(AI 对话、RAG 检索、图片上传等),都是这个范式的变体:前端发请求 → 后端处理 → 返回 JSON → 前端展示。
更多推荐
所有评论(0)