Skip to content

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=/api
js
// 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 清除定时器,防止内存泄漏

五、端到端验证流程

  1. 打开前端页面 http://localhost:5173,自动跳转到登录页
  2. admin/admin123 登录,Token 存入 localStorage
  3. 进入资产列表,能看到初始数据
  4. 点击"新增资产",填写表单,提交后列表自动刷新
  5. 进入任务管理,选择资产和操作,创建任务
  6. 任务状态从 pendingrunningdone,页面自动更新
  7. 用管理员账号进入事件日志,能看到操作记录

六、常见错误

代理配置不生效

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')如果 tasksref,需要确保引用不变。如果用 reactive 包裹数组,Vue 3 对数组索引变更有一定限制,建议用 tasks.value = [...tasks.value] 触发更新。