4. 前端项目初始化

4 min

1. 创建 Vue 3 项目

进入 ai-chat/ 目录,使用 Vite 脚手架创建前端项目:

bash
npm create vite@latest ai-chat-frontend -- --template vue-ts

构建完成后此时目录结构为:

text
ai-chat/
├── ai-chat-backend/      # 后端(前面创建)
└── ai-chat-frontend/     # 前端(本次创建)
    ├── public/
    ├── src/
    ├── index.html
    ├── package.json
    ├── vite.config.ts
    ├── tsconfig.json
    ├── tsconfig.app.json
    └── tsconfig.node.json

2. 清理脚手架默认内容

Vite 脚手架会生成示例代码和资源文件,我们需要先清理掉,从干净的状态开始。

删除不需要的文件:

bash
cd ai-chat-frontend

# 删除示例组件
rm -rf src/components/HelloWorld.vue

# 删除示例资源图片
rm -rf src/assets/hero.png
rm -rf src/assets/vite.svg
rm -rf src/assets/vue.svg

清空 src/style.css(后续由 Tailwind 接管)

重写 src/App.vue(删除对 HelloWorld 的引用)

vue
<template>
  <div>
    <h1>AI Chat</h1>
  </div>
</template>

src/main.ts 保持不动,脚手架生成的已经是最简形式:

typescript
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

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

添加 .vue 类型声明

新版 Vite 脚手架使用 TypeScript Project References 模式(tsconfig.json -> tsconfig.app.json),部分 IDE 在这种模式下无法正确识别 .vue 文件的类型,导致 import XxxView from '../views/XxxView.vue'TS2307: Cannot find module 错误。

创建 src/shims-vue.d.ts,显式声明 .vue 模块类型:

typescript
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

此时运行 npm run dev 应该能看到一个干净的页面,只显示 "AI Chat"。

3. 引入 DaisyUI

DaisyUI 是基于 Tailwind CSS 的组件库,提供了开箱即用的 UI 组件(按钮、输入框、卡片、模态框等),省去手写样式的时间。

安装 Tailwind CSS v4 和 DaisyUI 命令如下:

bash
npm install tailwindcss @tailwindcss/vite daisyui

配置 Vite 插件,编辑 vite.config.ts

typescript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    vue(),
    tailwindcss(),
  ],
})

src/style.css 中引入 Tailwind 和 DaisyUI:

css
@import "tailwindcss";
@plugin "daisyui";

4. 安装常用工具

bash
# HTTP 请求库
npm install axios

# 路由
npm install vue-router

# 状态管理
npm install pinia

简要说明:

  • Axios:HTTP 请求库,后续会配置拦截器自动携带 JWT Token
  • Vue Router:前端路由,管理页面导航和路由守卫
  • Pinia:Vue 3 官方推荐的状态管理库,用于管理全局数据

5. 规划项目结构

src/ 下创建以下目录:

bash
mkdir -p src/{api,views,stores,composables,router}
text
src/
├── api/            # 后端 API 请求模块
├── composables/    # 可复用的组合式函数
├── router/         # 路由配置
├── stores/         # Pinia 状态管理
├── views/          # 页面组件
├── App.vue
├── main.ts
└── style.css

每个目录的职责:

  • api/:按后端模块拆分请求函数,如 auth.tstopic.tschat.ts
  • views/:页面级组件,如 LoginView.vueChatView.vue
  • stores/:全局状态,如用户认证状态
  • composables/:可复用逻辑,如 SSE 流式请求
  • router/:路由表和导航守卫

6. Axios 封装

前面后端定义了统一响应格式 ApiResponse<T>,所有接口返回的 JSON 结构为 { code, message, data }。前端需要对应地做自动拆包,成功时直接取出 data,失败时抛出错误。

创建 src/api/index.ts

typescript
import axios from 'axios'

// 后端统一响应格式
interface ApiResponse<T = any> {
  code: number
  message: string
  data: T
}

const api = axios.create({
  baseURL: '/api',
  timeout: 30000,
})

// 请求拦截器:自动携带 JWT Token(后续实现登录后生效)
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// 响应拦截器:自动拆包 ApiResponse
api.interceptors.response.use(
  (response) => {
    const apiResponse = response.data as ApiResponse
    if (apiResponse.code === 200) {
      // 成功:将 response.data 替换为实际数据,调用方直接拿到业务数据
      response.data = apiResponse.data
      return response
    }
    // 业务错误(code !== 200 但 HTTP 状态码为 200)
    return Promise.reject(new Error(apiResponse.message))
  },
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token')
      window.location.href = '/login'
    }
    // 尝试从 ApiResponse 中提取错误信息
    const message = error.response?.data?.message || error.message
    return Promise.reject(new Error(message))
  }
)

export default api

7. 配置路由

创建 src/router/index.ts

typescript
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import LoginView from '../views/LoginView.vue'
import ChatView from '../views/ChatView.vue'

const routes: RouteRecordRaw[] = [
  {
    path: '/login',
    name: 'Login',
    component: LoginView,
  },
  {
    path: '/',
    name: 'Chat',
    component: ChatView,
  },
]

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

export default router

8. 配置 Pinia

创建 src/stores/index.ts

typescript
import { createPinia } from 'pinia'

const pinia = createPinia()

export default pinia

9. 更新入口文件

现在把 Router 和 Pinia 注册到应用中,编辑 src/main.ts

typescript
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import pinia from './stores'

const app = createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')

同时更新 src/App.vue,用 <RouterView /> 替换之前的占位内容:

vue
<script setup lang="ts">
  import { RouterView } from 'vue-router'
</script>

<template>
  <RouterView />
</template>

10. 创建占位页面

先创建两个最简页面,后续章节再填充具体内容。

src/views/LoginView.vue

vue
<template>
  <div class="min-h-screen flex items-center justify-center">
    <div class="card bg-base-200 w-96 shadow-xl">
      <div class="card-body items-center text-center">
        <h2 class="card-title">AI Chat</h2>
        <p>登录页面(待实现)</p>
      </div>
    </div>
  </div>
</template>

src/views/ChatView.vue

vue
<template>
  <div class="min-h-screen flex items-center justify-center">
    <h1 class="text-2xl">聊天页面(待实现)</h1>
  </div>
</template>

11. 配置 Vite 代理

开发时前端运行在 5173 端口,后端在 8080 端口。配置代理后,前端发往 /api 的请求会自动转发到后端,避免跨域问题。

编辑 vite.config.ts(在之前的基础上追加 server 配置):

typescript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    vue(),
    tailwindcss(),
  ],
  server: {
    port: 5173,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
      },
    },
  },
})

12. 验证前后端通信

确保后端正在运行,然后启动前端:

bash
npm run dev

打开浏览器访问 http://localhost:5173,应该能看到 DaisyUI 风格的登录占位卡片。

打开浏览器的开发者工具,在 Console 中执行:

javascript
fetch('/api/ping').then(r => r.json()).then(console.log)
// 应输出:{code: 200, message: "操作成功", data: "pong"}

如果看到正确的响应,说明前端请求成功代理到后端,前后端通信没问题。

13. 小结

从零搭建了 Vue 3 前端项目,完成了开发环境配置和前后端通信验证。

知识点 说明
项目创建 Vite 创建 Vue 3 + TypeScript 项目,清理脚手架默认内容
UI 框架 引入 DaisyUI(基于 Tailwind CSS 的组件库)
核心依赖 Axios(HTTP 请求)、Vue Router(路由)、Pinia(状态管理)
目录结构 按功能划分 api / views / stores / composables 等目录
Axios 拦截器 自动拆包后端 ApiResponse 统一响应格式
Vite 代理 /api 请求代理到后端 8080 端口,解决跨域问题

接下来实现用户认证功能,包括登录页面、JWT Token 存储和自动携带、路由守卫等。