02 搭建前端骨架
02 搭建前端骨架
这篇目标:创建一个能跑、能请求后端的 Vue3 工程。
1. 环境准备
- Node.js 20+
- pnpm
2. 创建 Vue3 项目
rtk pnpm create vue@latest data-agent-frontend推荐选项:
- TypeScript: yes
- Router: 可选
- Pinia: 可选
- ESLint/Prettier: 按团队习惯
3. 安装依赖并启动
rtk cd data-agent-frontend
rtk pnpm install
rtk pnpm add axios fs-extra adm-zip uuid4. 先在后端补一个 HelloController
前端联调不要一开始就手写请求地址,先让后端提供一个最小可验证接口。
@RestController
class HelloController {
data class Message(val message: String)
@GetMapping("/hello")
fun hello(): Message {
return Message("QiFan")
}
}后面前端会通过生成出来的 API SDK 调这个接口,所以这里先把后端最小闭环补齐。
5. 执行 pnpm run api
接下来不要直接在页面里写 axios.get('/api/hello'),而是先生成前端 API 代码。
因为 scripts/generate-api.js 会用到额外依赖,所以在执行前先确认下面这些包已经安装:
fs-extraadm-zipuuid
package.json
{
"scripts": {
"api": "node scripts/generate-api.js"
}
}执行:
rtk pnpm run api6. 介绍 data-agent-frontend/scripts/generate-api.js
这个脚本的职责很简单:从后端下载接口定义压缩包,解压到 src/apis/__generated,然后顺手把生成代码做一遍兼容处理。
以你当前项目里的实现为例,它做了这几件事:
- 从后端地址下载
ts.zip - 清空旧的
src/apis/__generated - 把压缩包解压到
src/apis/__generated - 遍历生成的
model和services - 替换
readonly、ReadonlyArray、ReadonlyMap等类型,减少前端使用时的阻力
这里会直接使用这些依赖:
fs-extra:删除旧的生成目录adm-zip:解压后端返回的ts.zipuuid:生成临时压缩包文件名
如果你的后端端口不是脚本里的默认值,记得先改 sourceUrl,例如改成:
const sourceUrl = 'http://localhost:9933/ts.zip'执行完成后,前端会拿到一份生成好的 Api 类和对应的类型定义。
7. 配置 request.ts、环境变量和 Vite 代理
建议不要在页面里直接散落 fetch('/api/...'),而是先收敛一个统一请求实例。
先加环境变量:
.env.development
VITE_API_PREFIX=/api.env.production
VITE_API_PREFIX=/api然后新增 src/utils/request.ts:
import axios from 'axios'
const BASE_URL = import.meta.env.VITE_API_PREFIX
export const request = axios.create({
baseURL: BASE_URL,
timeout: 30000,
})
request.interceptors.response.use(
(res) => {
return res.data
},
({ response }) => {
return Promise.reject(response.data)
},
)骨架阶段先保持这个最小版本就够了。后面如果接入登录态、统一错误提示、token 刷新,再继续补拦截器。
vite.config.ts 里把 /api 代理到后端:
export default defineConfig({
// ...
server: {
host: '0.0.0.0',
port: 3500,
proxy: {
'/api': {
target: 'http://localhost:9933',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
})8. 配置 src/utils/api-instance.ts
有了生成代码和统一请求实例之后,再补一层 API 实例,把两边接起来。
import { Api } from '@/apis/__generated'
import { request } from '@/utils/request'
export const api = new Api(async ({ uri, method, body }) => {
return await request({ url: uri, method, data: body })
})这一步的意义是:以后页面和 store 都不直接关心接口 URL,而是统一通过 api.xxxController.xxx() 调用。
9. 前端调用 api.helloController.hello()
到这里,前端就可以直接通过生成好的 SDK 调后端接口了:
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { api } from '@/utils/api-instance.ts'
const hello = ref('')
const loadHello = async () => {
const res = await api.helloController.hello()
hello.value = res.message
}
onMounted(() => {
loadHello()
})
</script>
<template>
<h1>Hello, {{ hello }}</h1>
</template>
<style scoped></style>如果这里能成功拿到后端返回值,说明这一条链路已经通了:
HelloController -> ts.zip -> pnpm run api -> request.ts -> api-instance.ts -> api.helloController.hello()
最后再启动前端:
rtk pnpm dev默认会在 http://localhost:3500 启动。
10. 常见问题
前端 404
/api/...
检查代理 target 与 rewrite。浏览器跨域错误
优先通过 Vite 代理解决,不要直接改后端跨域策略。rtk pnpm run api失败
先确认后端是否已经提供ts.zip导出能力,再检查generate-api.js里的下载地址、后端端口和目标目录。前端没有生成
helloController
先确认后端HelloController是否已经被接口导出工具扫描到,然后重新执行一次rtk pnpm run api。
