阶段 09:前端基础工程构建
合辑 :FastAPI × Vue3 JoyFit 系统阶段开发计划 | 端 :前端 | 阶段 :9 / 12 | 前置阶段 :阶段 08:后端测试与接口文档
大家好,我是小马不起床。
目录
- 一、阶段目标
- 二、本阶段开发顺序
- 三、最终目录结构
- 四、本阶段文件清单
- [五、步骤 1:创建 Vue3 前端项目](#五、步骤 1:创建 Vue3 前端项目)
- [六、步骤 2:安装前端业务依赖](#六、步骤 2:安装前端业务依赖)
- [七、步骤 3:配置 package.json](#七、步骤 3:配置 package.json)
- [八、步骤 4:配置 Vite](#八、步骤 4:配置 Vite)
- [九、步骤 5:配置开发环境变量](#九、步骤 5:配置开发环境变量)
- [十、步骤 6:配置前端入口](#十、步骤 6:配置前端入口)
- [十一、步骤 7:配置根组件](#十一、步骤 7:配置根组件)
- [十二、步骤 8:创建基础路由](#十二、步骤 8:创建基础路由)
- [十三、步骤 9:创建后端响应类型](#十三、步骤 9:创建后端响应类型)
- [十四、步骤 10:创建 Axios 请求封装](#十四、步骤 10:创建 Axios 请求封装)
- [十五、步骤 11:创建全局样式](#十五、步骤 11:创建全局样式)
- [十六、步骤 12:创建基础布局](#十六、步骤 12:创建基础布局)
- [十七、步骤 13:创建首页骨架](#十七、步骤 13:创建首页骨架)
- [十八、步骤 14:创建 404 页面](#十八、步骤 14:创建 404 页面)
- [十九、步骤 15:启动前端服务](#十九、步骤 15:启动前端服务)
- 二十、和后端联通的基础验证
- 二十一、本阶段验收清单
- 二十二、常见问题
- 二十三、进入下一阶段的条件
- 二十四、问题记录与修复方案
说明:文中尖括号
<>内容为占位符,使用时按实际环境替换。
一、阶段目标
本阶段目标是在后端接口基本稳定、OpenAPI 可以导出的基础上,创建 Vue3 前端工程,并完成前端基础设施。
完成本阶段后,前端应具备以下能力:
- 使用 Vite 创建 Vue3 + TypeScript 工程。
- 安装 Vue Router、Pinia、Axios、Element Plus。
- 配置开发环境 API 代理。
- 创建全局请求封装。
- 创建基础路由。
- 创建基础布局。
- 创建前端首页骨架。
- 可以通过
npm run dev启动前端服务。
本阶段仅搭建前端基础工程,不实现登录表单与权限守卫;登录权限在下一阶段实现。
二、本阶段开发顺序
text
1. 使用 Vite 创建 frontend 项目
2. 安装前端依赖
3. 配置 Vite 代理
4. 配置环境变量
5. 配置 main.ts
6. 配置 App.vue
7. 创建路由
8. 创建 Axios 请求封装
9. 创建基础布局
10. 创建首页和 404 页面
11. 启动前端服务验证
三、最终目录结构
完成本阶段后,前端核心目录如下:
text
frontend/
docs/
public/
src/
api/
http.ts
types.ts
layouts/
BasicLayout.vue
router/
index.ts
styles/
global.css
views/
HomeView.vue
NotFoundView.vue
App.vue
main.ts
.env.development
index.html
package.json
vite.config.ts
四、本阶段文件清单
| 序号 | 相对路径 | 类型 | 说明 |
|---|---|---|---|
| 1 | frontend/package.json |
修改 | 前端依赖与脚本 |
| 2 | frontend/vite.config.ts |
修改 | Vite 插件、别名、开发代理 |
| 3 | frontend/.env.development |
新增 | 开发环境变量 |
| 4 | frontend/src/main.ts |
修改 | Vue 入口、注册 Pinia、Router、Element Plus |
| 5 | frontend/src/App.vue |
修改 | 根组件 |
| 6 | frontend/src/router/index.ts |
新增 | 基础路由 |
| 7 | frontend/src/api/types.ts |
新增 | 后端统一响应类型 |
| 8 | frontend/src/api/http.ts |
新增 | Axios 实例与请求封装 |
| 9 | frontend/src/styles/global.css |
新增 | 全局样式 |
| 10 | frontend/src/layouts/BasicLayout.vue |
新增 | 基础布局 |
| 11 | frontend/src/views/HomeView.vue |
新增 | 首页骨架 |
| 12 | frontend/src/views/NotFoundView.vue |
新增 | 404 页面 |
五、步骤 1:创建 Vue3 前端项目
执行位置
确认当前目录为:
text
FastAPI_Vue3_JoyFit
即与 backend 同级。
创建命令
bash
npm create vite@latest frontend -- --template vue-ts
进入前端目录:
bash
cd frontend
安装 Vite 默认依赖:
bash
npm install
注意事项
- 项目名固定为
frontend。 - 模板选择
vue-ts,即 Vue3 + TypeScript。 - 若提示是否安装
create-vite,输入y。
六、步骤 2:安装前端业务依赖
执行位置
text
FastAPI_Vue3_JoyFit/frontend
安装命令
bash
npm install vue-router@4 pinia axios element-plus @element-plus/icons-vue
依赖说明
| 依赖 | 作用 |
|---|---|
vue-router |
前端路由(Vue Router) |
pinia |
全局状态管理(Pinia) |
axios |
HTTP 请求库(Axios) |
element-plus |
后台管理 UI 组件库(Element Plus) |
@element-plus/icons-vue |
Element Plus 图标 |
七、步骤 3:配置 package.json
相对路径
text
frontend/package.json
完整代码
json
{
"name": "joyfit-frontend",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
"build": "vue-tsc -b && vite build",
"preview": "vite preview --host 0.0.0.0"
},
"dependencies": {
"@element-plus/icons-vue": "latest",
"axios": "latest",
"element-plus": "latest",
"pinia": "latest",
"vue": "latest",
"vue-router": "latest"
},
"devDependencies": {
"@vitejs/plugin-vue": "latest",
"typescript": "latest",
"vite": "latest",
"vue-tsc": "latest"
}
}
注意事项
- 通过
npm create vite与npm install生成的版本号可能不是latest,属正常情况。 - 无需为对齐版本号而手动修改版本。
- 关键在于依赖项与
scripts字段完整。
八、步骤 4:配置 Vite
相对路径
text
frontend/vite.config.ts
完整代码
typescript
import { fileURLToPath, URL } from 'node:url'
import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
server: {
host: '0.0.0.0',
port: 5173,
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
},
},
},
})
注意事项
- 前端访问
/api时,Vite 会代理到后端http://127.0.0.1:8000。 - 开发阶段因此不需要直接处理浏览器跨域(CORS, Cross-Origin Resource Sharing)。
- 后端仍保留 CORS 配置,方便接口文档(Swagger, OpenAPI)与其他工具访问。
九、步骤 5:配置开发环境变量
相对路径
text
frontend/.env.development
完整代码
text
VITE_APP_TITLE=JoyFit
VITE_API_BASE_URL=/api
注意事项
- Vite 前端环境变量必须以
VITE_开头。 - 开发环境使用
/api,由 Vite 代理到后端。 - 生产环境可再创建
.env.production。
十、步骤 6:配置前端入口
相对路径
text
frontend/src/main.ts
完整代码
typescript
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import './styles/global.css'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(ElementPlus)
app.mount('#app')
注意事项
Pinia在本阶段先注册,下一阶段的登录状态会使用。router在本阶段先注册,下一阶段会增加登录页与路由守卫(Navigation Guard)。ElementPlus全局注册,便于直接使用组件。
十一、步骤 7:配置根组件
相对路径
text
frontend/src/App.vue
完整代码
vue
<template>
<RouterView />
</template>
注意事项
- 根组件仅负责渲染路由出口。
- 布局放入
layouts/BasicLayout.vue,避免根组件过重。
十二、步骤 8:创建基础路由
相对路径
text
frontend/src/router/index.ts
完整代码
typescript
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('../layouts/BasicLayout.vue'),
children: [
{
path: '',
name: 'home',
component: () => import('../views/HomeView.vue'),
meta: {
title: '首页',
},
},
],
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('../views/NotFoundView.vue'),
meta: {
title: '页面不存在',
},
},
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
})
router.afterEach((to) => {
const title = to.meta.title ? `${String(to.meta.title)} - JoyFit` : 'JoyFit'
document.title = title
})
export default router
注意事项
- 当前仅配置首页与 404。
- 登录页、后台页面、用户端页面在后续阶段逐步加入。
BasicLayout作为主布局,后续后台菜单挂载于此。
十三、步骤 9:创建后端响应类型
相对路径
text
frontend/src/api/types.ts
完整代码
typescript
export interface ApiResponse<T = unknown> {
code: number
message: string
data: T
}
export interface PageResult<T> {
items: T[]
total: number
page: number
page_size: number
pages: number
}
注意事项
- 后端统一返回
code、message、data。 - 分页结构和后端
paginate返回字段保持一致。
十四、步骤 10:创建 Axios 请求封装
相对路径
text
frontend/src/api/http.ts
完整代码
typescript
import axios, {
type AxiosError,
type AxiosInstance,
type AxiosRequestConfig,
type InternalAxiosRequestConfig,
} from 'axios'
import { ElMessage } from 'element-plus'
import type { ApiResponse } from './types'
const http: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 10000,
})
http.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem('joyfit_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
http.interceptors.response.use(
(response) => {
const body = response.data as ApiResponse
if (body && typeof body.code === 'number' && body.code !== 200) {
ElMessage.error(body.message || '请求失败')
return Promise.reject(body)
}
return response
},
(error: AxiosError<ApiResponse>) => {
const status = error.response?.status
const message = error.response?.data?.message || error.message || '网络请求失败'
if (status !== 401) {
ElMessage.error(message)
}
return Promise.reject(error)
},
)
export async function request<T = unknown>(config: AxiosRequestConfig): Promise<T> {
const response = await http.request<ApiResponse<T>>(config)
return response.data.data
}
export default http
注意事项
- 令牌(Token)暂从
localStorage.joyfit_token读取,下一阶段由登录模块写入;Bearer <访问令牌>为占位写法,实际值由请求拦截器拼接。 - 后端业务错误若
code !== 200,前端统一提示。 - HTTP 401 暂不弹出错误提示,下一阶段由登录权限模块统一处理跳转。
十五、步骤 11:创建全局样式
相对路径
text
frontend/src/styles/global.css
完整代码
css
:root {
font-family:
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
color: #1f2937;
background: #f6f7fb;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
}
html,
body,
#app {
width: 100%;
min-width: 320px;
min-height: 100%;
margin: 0;
}
body {
min-height: 100vh;
}
a {
color: inherit;
text-decoration: none;
}
button,
input,
textarea,
select {
font: inherit;
}
.page-panel {
width: 100%;
}
.section-title {
margin: 0;
color: #111827;
font-size: 20px;
font-weight: 700;
line-height: 1.4;
}
.section-subtitle {
margin: 6px 0 0;
color: #6b7280;
font-size: 14px;
line-height: 1.6;
}
注意事项
- 全局样式保持精简,便于后续后台页面风格统一。
- 禁止在全局样式中编写复杂业务组件样式。
十六、步骤 12:创建基础布局
相对路径
text
frontend/src/layouts/BasicLayout.vue
完整代码
vue
<template>
<div class="app-shell">
<aside class="sidebar">
<div class="brand">
<span class="brand-mark">J</span>
<span class="brand-name">JoyFit</span>
</div>
<nav class="nav-list">
<RouterLink class="nav-item" to="/">工作台</RouterLink>
</nav>
</aside>
<section class="workspace">
<header class="topbar">
<div>
<p class="eyebrow">FastAPI + Vue3</p>
<h1>JoyFit 管理端</h1>
</div>
<el-tag type="success" effect="light">Backend Ready</el-tag>
</header>
<main class="content-area">
<RouterView />
</main>
</section>
</div>
</template>
<style scoped>
.app-shell {
display: grid;
min-height: 100vh;
grid-template-columns: 240px minmax(0, 1fr);
background: #f6f7fb;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 28px;
border-right: 1px solid #e5e7eb;
background: #ffffff;
padding: 24px 18px;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
min-height: 40px;
}
.brand-mark {
display: inline-flex;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
border-radius: 8px;
background: #111827;
color: #ffffff;
font-size: 18px;
font-weight: 800;
}
.brand-name {
color: #111827;
font-size: 18px;
font-weight: 800;
}
.nav-list {
display: grid;
gap: 8px;
}
.nav-item {
display: flex;
min-height: 40px;
align-items: center;
border-radius: 8px;
padding: 0 12px;
color: #4b5563;
font-size: 14px;
font-weight: 600;
}
.nav-item.router-link-active {
background: #eef2ff;
color: #3730a3;
}
.workspace {
min-width: 0;
}
.topbar {
display: flex;
min-height: 88px;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #e5e7eb;
background: #ffffff;
padding: 0 28px;
}
.eyebrow {
margin: 0;
color: #6b7280;
font-size: 13px;
font-weight: 600;
}
.topbar h1 {
margin: 4px 0 0;
color: #111827;
font-size: 22px;
line-height: 1.3;
}
.content-area {
padding: 28px;
}
@media (max-width: 760px) {
.app-shell {
grid-template-columns: 1fr;
}
.sidebar {
gap: 14px;
border-right: 0;
border-bottom: 1px solid #e5e7eb;
padding: 16px;
}
.topbar {
align-items: flex-start;
flex-direction: column;
gap: 10px;
padding: 18px 16px;
}
.content-area {
padding: 18px 16px;
}
}
</style>
注意事项
- 当前仅有一个"工作台"菜单。
- 登录权限阶段将依据
/api/auth/menus动态生成菜单。 - 布局保留移动端适配,避免后续页面被侧边栏挤压。
十七、步骤 13:创建首页骨架
相对路径
text
frontend/src/views/HomeView.vue
完整代码
vue
<script setup lang="ts">
import { Connection, DataBoard, DocumentChecked, Operation } from '@element-plus/icons-vue'
const modules = [
{
title: '接口文档',
description: '后端 Swagger 与 OpenAPI 已准备好,可以进入前端对接。',
icon: DocumentChecked,
},
{
title: '业务模块',
description: '用户、门店、课程、动作、活动首页接口已完成基础验收。',
icon: DataBoard,
},
{
title: '请求封装',
description: 'Axios 已统一处理 Token、业务错误和网络错误。',
icon: Connection,
},
{
title: '下一阶段',
description: '继续实现登录页、Token 状态、路由守卫和动态菜单。',
icon: Operation,
},
]
</script>
<template>
<section class="page-panel">
<div class="hero-band">
<div>
<h2 class="section-title">前端基础工程</h2>
<p class="section-subtitle">
当前阶段完成工程骨架、路由、状态管理、请求封装和基础布局。
</p>
</div>
</div>
<div class="module-grid">
<article v-for="item in modules" :key="item.title" class="module-card">
<el-icon class="module-icon">
<component :is="item.icon" />
</el-icon>
<div>
<h3>{{ item.title }}</h3>
<p>{{ item.description }}</p>
</div>
</article>
</div>
</section>
</template>
<style scoped>
.hero-band {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #ffffff;
padding: 24px;
}
.module-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
margin-top: 18px;
}
.module-card {
display: grid;
min-height: 148px;
grid-template-columns: 40px minmax(0, 1fr);
gap: 14px;
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #ffffff;
padding: 18px;
}
.module-icon {
width: 40px;
height: 40px;
border-radius: 8px;
background: #ecfeff;
color: #0891b2;
font-size: 22px;
}
.module-card h3 {
margin: 0;
color: #111827;
font-size: 16px;
line-height: 1.4;
}
.module-card p {
margin: 8px 0 0;
color: #6b7280;
font-size: 14px;
line-height: 1.6;
}
@media (max-width: 1120px) {
.module-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 640px) {
.module-grid {
grid-template-columns: 1fr;
}
}
</style>
注意事项
- 首页仅为工程状态面板,不发起业务数据请求。
- 正式首页内容在用户端页面阶段对接
/api/home。
十八、步骤 14:创建 404 页面
相对路径
text
frontend/src/views/NotFoundView.vue
完整代码
vue
<template>
<section class="not-found">
<div>
<p class="code">404</p>
<h2>页面不存在</h2>
<p>当前访问的页面还没有配置路由。</p>
<el-button type="primary" @click="$router.push('/')">返回首页</el-button>
</div>
</section>
</template>
<style scoped>
.not-found {
display: grid;
min-height: calc(100vh - 144px);
place-items: center;
text-align: center;
}
.code {
margin: 0;
color: #3730a3;
font-size: 48px;
font-weight: 800;
line-height: 1;
}
.not-found h2 {
margin: 14px 0 8px;
color: #111827;
font-size: 24px;
}
.not-found p {
margin: 0 0 18px;
color: #6b7280;
}
</style>
注意事项
- 404 页面在本阶段完成,后续路由调整时便于发现路径配置错误。
$router可在模板中直接使用。
十九、步骤 15:启动前端服务
启动命令
确认当前目录为:
text
FastAPI_Vue3_JoyFit/frontend
执行:
bash
npm run dev
访问地址
text
http://127.0.0.1:5173
若 5173 端口被占用,Vite 会提示新的端口,以终端输出为准。
二十、和后端联通的基础验证
启动后端:
bash
cd backend
source .venv/bin/activate
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
随后启动前端:
bash
cd frontend
npm run dev
验证:
- 浏览器访问
http://127.0.0.1:5173。 - 页面显示 JoyFit 管理端基础布局。
- 浏览器控制台无报错信息。
- 浏览器网络面板(Network)中访问
/api的请求由 Vite 代理转发到后端。
本阶段尚无页面主动请求后端接口,仅验证工程可启动、代理配置已存在。
二十一、本阶段验收清单
- 前端目录:
frontend创建成功 - 依赖安装:
npm install成功 - Vite 配置:
/api代理到后端 - 环境变量:
.env.development存在 - Vue 入口:
main.ts注册 Pinia、Router、Element Plus - 路由:首页与 404 页面可访问
- 请求封装:
src/api/http.ts存在 - 基础布局:首页显示基础管理端布局
- 启动验证:
npm run dev成功 - 构建验证:
npm run build成功
二十二、常见问题
1. npm create vite@latest 很慢或失败
原因:
网络连接 npm 镜像仓库(npm registry)不稳定。
修复:
稍后重试,或切换本机常用的 npm 镜像源。
2. npm run dev 提示端口被占用
原因:
5173 端口已被其他前端服务占用。
修复:
使用 Vite 提示的新端口访问,或关闭占用该端口的进程。
3. 页面空白
原因:
常见原因为 main.ts、router/index.ts 或组件路径配置错误。
修复:
- 打开浏览器控制台查看报错信息。
- 确认
src/router/index.ts存在。 - 确认
BasicLayout.vue、HomeView.vue、NotFoundView.vue文件路径正确。
4. TypeScript 提示找不到模块
原因:
依赖未安装,或 IDE(集成开发环境)未刷新 TypeScript 服务。
修复:
执行:
bash
npm install
随后重启编辑器的 TypeScript 服务。
5. 前端请求 /api 失败
原因:
后端未启动,或 Vite 代理(proxy)配置不正确。
修复:
- 确认后端运行在
http://127.0.0.1:8000。 - 确认
vite.config.ts中已配置/api代理。 - 重新启动
npm run dev。
二十三、进入下一阶段的条件
满足以下条件后,进入第十阶段:
必须满足:
frontend项目创建完成。npm install成功。npm run dev成功。- 首页可正常显示。
npm run build成功。- Axios 请求封装存在。
- Vite
/api代理配置完成。
以上条件全部满足后,进入下一阶段:阶段 10:前端登录权限与布局。
二十四、问题记录与修复方案
后续在第九阶段实践中遇到问题,可完整记录报错原文,便于检索与定位。
问题按以下两个步骤处理:
- 判断错误属于 Vite 创建、依赖安装、TypeScript、路由、Element Plus、Axios 还是代理配置问题。
- 将修复办法追加到本文件底部。
问题追加格式
text
### 问题 N:问题标题
#### 报错现象
按下方模板记录报错信息。
#### 原因分析
这里说明为什么会报错。
#### 修复办法
这里给出具体修复步骤。
#### 需要修改的文件
- 相对路径:xxx
- 修改说明:xxx
- 完整代码或补丁:xxx
问题 1:Vue 报 Element is missing end tag
报错现象
执行:
bash
npm run dev
Vite 报错:
text
Internal server error: Element is missing end tag.
Plugin: vite:vue
File: frontend/src/layouts/BasicLayout.vue
或:
text
File: frontend/src/views/HomeView.vue
原因分析
BasicLayout.vue、HomeView.vue 或 NotFoundView.vue 的 <style scoped> 未编写结束标签:
vue
</style>
Vue 单文件组件(SFC, Single File Component)必须保证 <template>、<script>、<style> 标签完整闭合。编译器通常在解析到 <style scoped> 时才发现前面的结构未完整结束,因此报错位置看似位于 CSS,实际是 SFC 标签未闭合。
修复办法
在每个 .vue 文件的样式(CSS)末尾补充:
vue
</style>
需要修改的文件
- 相对路径:
frontend/src/layouts/BasicLayout.vue - 修改说明:在文件末尾补充
</style> - 补丁:
vue
@media (max-width: 760px) {
.content-area {
padding: 18px 16px;
}
}
</style>
- 相对路径:
frontend/src/views/HomeView.vue - 修改说明:在文件末尾补充
</style> - 补丁:
vue
@media (max-width: 640px) {
.module-grid {
grid-template-columns: 1fr;
}
}
</style>
- 相对路径:
frontend/src/views/NotFoundView.vue - 修改说明:若该文件同样按本文档创建,也在文件末尾补充
</style> - 补丁:
vue
.not-found p {
margin: 0 0 18px;
color: #6b7280;
}
</style>
处理完后再验证
重新运行:
bash
npm run dev
若标签闭合正确,Element is missing end tag 提示消失,页面可正常编译。
问题 2:TS2307 找不到 .vue 组件模块
报错现象
TypeScript 报错:
text
TS2307: Cannot find module '../layouts/BasicLayout.vue' or its corresponding type declarations.
TS2307: Cannot find module '../views/HomeView.vue' or its corresponding type declarations.
TS2307: Cannot find module '../views/NotFoundView.vue' or its corresponding type declarations.
原因分析
router/index.ts 中导入的是 .vue 单文件组件:
typescript
component: () => import('../layouts/BasicLayout.vue')
若项目缺少 Vite/Vue 的类型声明文件(Type Declaration),TypeScript 无法识别 .vue 文件可作为模块导入,即报 TS2307。
Vite 创建的 Vue + TypeScript 项目通常会生成 src/vite-env.d.ts,若该文件被删除或未生成,需要手动补充。
修复办法
新增 frontend/src/vite-env.d.ts,声明 Vite 环境类型与 .vue 模块类型。
需要修改的文件
- 相对路径:
frontend/src/vite-env.d.ts - 修改说明:新增 Vue 单文件组件类型声明
- 完整代码:
typescript
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<Record<string, never>, Record<string, never>, unknown>
export default component
}
处理完后再验证
执行:
bash
npm run build
若类型声明生效,router/index.ts 中导入 .vue 文件的 TS2307 报错消失。