Appearance
57|前后端联调与任务调度
前后端各自能跑之后,需要把它们连起来:解决跨域、调试接口对接、验证任务调度的完整链路。这一篇把开发环境的联调配置、常见问题排查,以及任务状态的轮询更新一起处理。
一、开发环境联调
Vite 代理配置
前端开发服务器在 5173,后端在 8000,浏览器跨域。配置 Vite 代理把 /api 请求转发到后端:
js
// frontend/vite.config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
"/api": {
target: "http://localhost:8000",
changeOrigin: true,
},
},
},
});配置后,前端代码里请求 /api/servers 会被 Vite 代理到 http://localhost:8000/api/servers,浏览器看到的是同源请求,不触发 CORS。
生产环境没有 Vite 代理,需要 Nginx 做反向代理(58 篇讲)。
环境变量
bash
# frontend/.env.development
VITE_API_URL=/api
# frontend/.env.production
VITE_API_URL=/apijs
// request.js
const BASE_URL = import.meta.env.VITE_API_URL || "";二、CORS 配置
如果不用代理(如前端直接访问 http://localhost:8000),后端需要开 CORS:
python
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)三、联调常见问题
接口返回 422
前端请求体格式和后端 Pydantic 模型不匹配。打开浏览器开发者工具的 Network 面板,查看响应中的 detail,定位是哪个字段校验失败。
Token 丢失
登录成功后前端保存了 Token,但刷新页面后状态丢失。检查 Pinia store 是否正确从 localStorage 恢复:
js
const token = ref(localStorage.getItem("token") || "");状态码正确但无数据
后端返回 200,但前端 response.json() 报错。可能是后端返回了空字符串或非 JSON 内容。检查后端是否在所有分支都返回了合法 JSON。
时间格式问题
后端返回 ISO 格式时间字符串(2026-06-27T14:32:00),前端直接显示不友好。用 toLocaleString 转换:
js
new Date("2026-06-27T14:32:00").toLocaleString("zh-CN");
// "2026/6/27 14:32:00"四、任务状态轮询
任务创建后是 pending 状态,后台执行后变为 running 再变为 done。前端需要轮询任务状态:
vue
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import { api } from "../api/request.js";
const tasks = ref([]);
let timer = null;
async function loadTasks() {
tasks.value = await api.get("/tasks");
}
function startPolling() {
timer = setInterval(() => {
const hasPending = tasks.value.some((t) => ["pending", "running"].includes(t.status));
if (hasPending) {
loadTasks();
}
}, 3000);
}
onMounted(() => {
loadTasks();
startPolling();
});
onUnmounted(() => {
clearInterval(timer);
});
</script>| 参数 | 选择 |
|---|---|
| 轮询间隔 | 3~5 秒,太短增加服务器压力,太长用户体验差 |
| 停止条件 | 没有 pending/running 任务时停止轮询,节省资源 |
| 页面离开 | onUnmounted 清除定时器,防止内存泄漏 |
五、端到端验证流程
- 打开前端页面
http://localhost:5173,自动跳转到登录页 - 用
admin/admin123登录,Token 存入localStorage - 进入资产列表,能看到初始数据
- 点击"新增资产",填写表单,提交后列表自动刷新
- 进入任务管理,选择资产和操作,创建任务
- 任务状态从
pending→running→done,页面自动更新 - 用管理员账号进入事件日志,能看到操作记录
六、常见错误
代理配置不生效
js
// 错误:proxy 写在了 build 配置里
export default defineConfig({
build: {
proxy: { ... } // 不对,proxy 应该在 server 下
}
});
// 正确
export default defineConfig({
server: {
proxy: { ... }
}
});轮询没有清除
js
// 错误:组件卸载后定时器还在跑
setInterval(() => { ... }, 3000);
// 正确
const timer = setInterval(() => { ... }, 3000);
onUnmounted(() => clearInterval(timer));任务状态更新但页面没反应
检查 Vue 的响应式:直接修改数组元素属性(task.status = 'done')如果 tasks 是 ref,需要确保引用不变。如果用 reactive 包裹数组,Vue 3 对数组索引变更有一定限制,建议用 tasks.value = [...tasks.value] 触发更新。