686 lines
17 KiB
Markdown
686 lines
17 KiB
Markdown
|
|
# GameGroup 前端架构设计文档
|
||
|
|
|
||
|
|
**项目名称**: GameGroup 前端系统
|
||
|
|
**文档版本**: v1.0
|
||
|
|
**更新时间**: 2026-01-28
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 目录
|
||
|
|
|
||
|
|
- [1. 项目概述](#1-项目概述)
|
||
|
|
- [2. 技术栈选型](#2-技术栈选型)
|
||
|
|
- [3. 项目架构](#3-项目架构)
|
||
|
|
- [4. 目录结构](#4-目录结构)
|
||
|
|
- [5. 核心设计原则](#5-核心设计原则)
|
||
|
|
- [6. 状态管理](#6-状态管理)
|
||
|
|
- [7. 路由设计](#7-路由设计)
|
||
|
|
- [8. API请求层](#8-api请求层)
|
||
|
|
- [9. 性能优化](#9-性能优化)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. 项目概述
|
||
|
|
|
||
|
|
GameGroup 是一个游戏社群管理平台,为玩家提供小组管理、游戏预约、积分系统、竞猜等功能。前端项目采用现代化的技术栈,注重用户体验和性能优化。
|
||
|
|
|
||
|
|
### 核心功能模块
|
||
|
|
|
||
|
|
- **认证模块**: 用户注册、登录、令牌刷新
|
||
|
|
- **用户中心**: 个人资料管理、设置
|
||
|
|
- **小组管理**: 创建/加入小组、成员管理、权限控制
|
||
|
|
- **游戏库**: 游戏浏览、搜索、筛选
|
||
|
|
- **预约系统**: 创建预约、加入/退出预约、时间管理
|
||
|
|
- **账目管理**: 账目记录、统计分析
|
||
|
|
- **排班助手**: 空闲时间提交、共同时间查找
|
||
|
|
- **荣誉墙**: 荣誉展示、时间轴
|
||
|
|
- **资产管理**: 资产借用/归还、流转记录
|
||
|
|
- **积分系统**: 积分查询、排行榜
|
||
|
|
- **竞猜系统**: 创建竞猜、参与竞猜、结算
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. 技术栈选型
|
||
|
|
|
||
|
|
### 核心框架
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"framework": "Vue 3",
|
||
|
|
"buildTool": "Vite",
|
||
|
|
"language": "TypeScript",
|
||
|
|
"router": "Vue Router 4",
|
||
|
|
"stateManagement": "Pinia",
|
||
|
|
"uiFramework": "Element Plus / Ant Design Vue",
|
||
|
|
"cssFramework": "Tailwind CSS / UnoCSS",
|
||
|
|
"http": "Axios"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 选型理由
|
||
|
|
|
||
|
|
#### Vue 3 + TypeScript
|
||
|
|
- **组合式 API**: 更好的逻辑复用和代码组织
|
||
|
|
- **类型安全**: TypeScript提供完整的类型推导和检查
|
||
|
|
- **性能提升**: 更小的包体积、更快的渲染
|
||
|
|
- **生态成熟**: 丰富的插件和工具支持
|
||
|
|
|
||
|
|
#### Vite
|
||
|
|
- **极速开发**: 基于ESM的即时热更新
|
||
|
|
- **快速构建**: Rollup打包,生产环境优化
|
||
|
|
- **现代化**: 原生支持ES模块、TypeScript、JSX
|
||
|
|
|
||
|
|
#### Pinia
|
||
|
|
- **Vue 3官方推荐**: 替代Vuex的下一代状态管理
|
||
|
|
- **类型友好**: 完整的TypeScript支持
|
||
|
|
- **轻量简洁**: API设计简洁,学习成本低
|
||
|
|
- **Devtools**: 优秀的开发者工具集成
|
||
|
|
|
||
|
|
#### Vue Router 4
|
||
|
|
- **官方路由**: Vue生态标准路由方案
|
||
|
|
- **动态路由**: 支持路由级代码分割
|
||
|
|
- **导航守卫**: 完善的权限控制
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. 项目架构
|
||
|
|
|
||
|
|
### 3.1 分层架构
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────┐
|
||
|
|
│ Presentation Layer │
|
||
|
|
│ (Components / Views / Layouts) │
|
||
|
|
└─────────────────────────────────────┘
|
||
|
|
↓
|
||
|
|
┌─────────────────────────────────────┐
|
||
|
|
│ Business Logic Layer │
|
||
|
|
│ (Composables / Stores / Hooks) │
|
||
|
|
└─────────────────────────────────────┘
|
||
|
|
↓
|
||
|
|
┌─────────────────────────────────────┐
|
||
|
|
│ Data Access Layer │
|
||
|
|
│ (API Services / Local Storage) │
|
||
|
|
└─────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 模块化设计
|
||
|
|
|
||
|
|
每个功能模块包含:
|
||
|
|
- **Views**: 页面级组件
|
||
|
|
- **Components**: 模块内组件
|
||
|
|
- **Composables**: 业务逻辑复用
|
||
|
|
- **Stores**: 状态管理
|
||
|
|
- **API**: 接口请求
|
||
|
|
- **Types**: TypeScript类型定义
|
||
|
|
- **Utils**: 工具函数
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. 目录结构
|
||
|
|
|
||
|
|
```
|
||
|
|
GameGroupFE/
|
||
|
|
├── public/ # 静态资源
|
||
|
|
│ ├── favicon.ico
|
||
|
|
│ └── ...
|
||
|
|
├── src/
|
||
|
|
│ ├── assets/ # 资源文件
|
||
|
|
│ │ ├── images/ # 图片
|
||
|
|
│ │ ├── icons/ # 图标
|
||
|
|
│ │ └── styles/ # 全局样式
|
||
|
|
│ │ ├── variables.scss # 样式变量
|
||
|
|
│ │ ├── mixins.scss # 样式混入
|
||
|
|
│ │ └── global.scss # 全局样式
|
||
|
|
│ ├── components/ # 公共组件
|
||
|
|
│ │ ├── common/ # 通用组件
|
||
|
|
│ │ │ ├── Button/
|
||
|
|
│ │ │ ├── Input/
|
||
|
|
│ │ │ ├── Modal/
|
||
|
|
│ │ │ └── Loading/
|
||
|
|
│ │ ├── business/ # 业务组件
|
||
|
|
│ │ │ ├── UserCard/
|
||
|
|
│ │ │ ├── GroupCard/
|
||
|
|
│ │ │ └── GameCard/
|
||
|
|
│ │ └── layout/ # 布局组件
|
||
|
|
│ │ ├── Header/
|
||
|
|
│ │ ├── Sidebar/
|
||
|
|
│ │ └── Footer/
|
||
|
|
│ ├── composables/ # 组合式函数
|
||
|
|
│ │ ├── useAuth.ts # 认证相关
|
||
|
|
│ │ ├── useRequest.ts # 请求封装
|
||
|
|
│ │ ├── usePagination.ts # 分页逻辑
|
||
|
|
│ │ └── useWebSocket.ts # WebSocket
|
||
|
|
│ ├── stores/ # Pinia状态管理
|
||
|
|
│ │ ├── auth.ts # 认证状态
|
||
|
|
│ │ ├── user.ts # 用户状态
|
||
|
|
│ │ ├── group.ts # 小组状态
|
||
|
|
│ │ └── app.ts # 应用状态
|
||
|
|
│ ├── api/ # API接口
|
||
|
|
│ │ ├── index.ts # Axios配置
|
||
|
|
│ │ ├── auth.ts # 认证接口
|
||
|
|
│ │ ├── user.ts # 用户接口
|
||
|
|
│ │ ├── group.ts # 小组接口
|
||
|
|
│ │ └── ...
|
||
|
|
│ ├── router/ # 路由配置
|
||
|
|
│ │ ├── index.ts # 路由入口
|
||
|
|
│ │ ├── routes/ # 路由模块
|
||
|
|
│ │ └── guards.ts # 路由守卫
|
||
|
|
│ ├── views/ # 页面视图
|
||
|
|
│ │ ├── auth/ # 认证相关页面
|
||
|
|
│ │ │ ├── Login.vue
|
||
|
|
│ │ │ └── Register.vue
|
||
|
|
│ │ ├── home/ # 首页
|
||
|
|
│ │ │ └── index.vue
|
||
|
|
│ │ ├── user/ # 用户中心
|
||
|
|
│ │ │ ├── Profile.vue
|
||
|
|
│ │ │ └── Settings.vue
|
||
|
|
│ │ ├── group/ # 小组管理
|
||
|
|
│ │ │ ├── List.vue
|
||
|
|
│ │ │ ├── Detail.vue
|
||
|
|
│ │ │ └── Create.vue
|
||
|
|
│ │ ├── game/ # 游戏库
|
||
|
|
│ │ ├── appointment/ # 预约管理
|
||
|
|
│ │ └── ...
|
||
|
|
│ ├── types/ # TypeScript类型定义
|
||
|
|
│ │ ├── api.ts # API响应类型
|
||
|
|
│ │ ├── user.ts # 用户类型
|
||
|
|
│ │ ├── group.ts # 小组类型
|
||
|
|
│ │ └── ...
|
||
|
|
│ ├── utils/ # 工具函数
|
||
|
|
│ │ ├── request.ts # 请求工具
|
||
|
|
│ │ ├── storage.ts # 存储工具
|
||
|
|
│ │ ├── format.ts # 格式化工具
|
||
|
|
│ │ └── validate.ts # 验证工具
|
||
|
|
│ ├── constants/ # 常量定义
|
||
|
|
│ │ ├── enums.ts # 枚举
|
||
|
|
│ │ └── config.ts # 配置
|
||
|
|
│ ├── directives/ # 自定义指令
|
||
|
|
│ │ └── permission.ts
|
||
|
|
│ ├── App.vue # 根组件
|
||
|
|
│ └── main.ts # 应用入口
|
||
|
|
├── tests/ # 测试文件
|
||
|
|
│ ├── unit/ # 单元测试
|
||
|
|
│ └── e2e/ # E2E测试
|
||
|
|
├── .env.development # 开发环境变量
|
||
|
|
├── .env.production # 生产环境变量
|
||
|
|
├── .eslintrc.cjs # ESLint配置
|
||
|
|
├── .prettierrc # Prettier配置
|
||
|
|
├── tsconfig.json # TypeScript配置
|
||
|
|
├── vite.config.ts # Vite配置
|
||
|
|
├── tailwind.config.js # Tailwind配置
|
||
|
|
└── package.json # 项目配置
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. 核心设计原则
|
||
|
|
|
||
|
|
### 5.1 组件设计原则
|
||
|
|
|
||
|
|
#### 单一职责原则
|
||
|
|
每个组件只负责一个功能,保持组件的独立性和可复用性。
|
||
|
|
|
||
|
|
#### 组合优于继承
|
||
|
|
使用组合式API和props/events进行组件通信,避免复杂的继承关系。
|
||
|
|
|
||
|
|
#### 容器组件与展示组件分离
|
||
|
|
- **容器组件**: 处理业务逻辑和状态管理
|
||
|
|
- **展示组件**: 纯UI渲染,通过props接收数据
|
||
|
|
|
||
|
|
### 5.2 代码规范
|
||
|
|
|
||
|
|
#### 命名规范
|
||
|
|
- **组件**: PascalCase (UserCard.vue)
|
||
|
|
- **文件**: kebab-case (user-profile.ts)
|
||
|
|
- **变量/函数**: camelCase (getUserInfo)
|
||
|
|
- **常量**: UPPER_SNAKE_CASE (API_BASE_URL)
|
||
|
|
- **类型/接口**: PascalCase (UserProfile)
|
||
|
|
|
||
|
|
#### 文件组织
|
||
|
|
- 一个文件只导出一个主要内容
|
||
|
|
- 相关文件放在同一目录
|
||
|
|
- 使用index.ts统一导出
|
||
|
|
|
||
|
|
#### 注释规范
|
||
|
|
```typescript
|
||
|
|
/**
|
||
|
|
* 获取用户信息
|
||
|
|
* @param userId - 用户ID
|
||
|
|
* @returns 用户信息对象
|
||
|
|
*/
|
||
|
|
async function getUserInfo(userId: string): Promise<User> {
|
||
|
|
// 实现
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. 状态管理
|
||
|
|
|
||
|
|
### 6.1 Pinia Store设计
|
||
|
|
|
||
|
|
#### 模块化Store
|
||
|
|
每个业务模块创建独立的Store:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// stores/user.ts
|
||
|
|
export const useUserStore = defineStore('user', () => {
|
||
|
|
// State
|
||
|
|
const userInfo = ref<UserInfo | null>(null)
|
||
|
|
const isLoading = ref(false)
|
||
|
|
|
||
|
|
// Getters
|
||
|
|
const isLoggedIn = computed(() => !!userInfo.value)
|
||
|
|
|
||
|
|
// Actions
|
||
|
|
async function fetchUserInfo() {
|
||
|
|
isLoading.value = true
|
||
|
|
try {
|
||
|
|
const data = await userApi.getProfile()
|
||
|
|
userInfo.value = data
|
||
|
|
} finally {
|
||
|
|
isLoading.value = false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
userInfo,
|
||
|
|
isLoading,
|
||
|
|
isLoggedIn,
|
||
|
|
fetchUserInfo
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 全局Store
|
||
|
|
应用级别的状态:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// stores/app.ts
|
||
|
|
export const useAppStore = defineStore('app', () => {
|
||
|
|
const theme = ref<Theme>('light')
|
||
|
|
const language = ref<Language>('zh-CN')
|
||
|
|
const sidebarCollapsed = ref(false)
|
||
|
|
|
||
|
|
function toggleTheme() {
|
||
|
|
theme.value = theme.value === 'light' ? 'dark' : 'light'
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
theme,
|
||
|
|
language,
|
||
|
|
sidebarCollapsed,
|
||
|
|
toggleTheme
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.2 持久化策略
|
||
|
|
|
||
|
|
使用`pinia-plugin-persistedstate`实现状态持久化:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// pinia.ts
|
||
|
|
import { createPinia } from 'pinia'
|
||
|
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||
|
|
|
||
|
|
const pinia = createPinia()
|
||
|
|
pinia.use(piniaPluginPersistedstate)
|
||
|
|
|
||
|
|
export default pinia
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. 路由设计
|
||
|
|
|
||
|
|
### 7.1 路由结构
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// router/index.ts
|
||
|
|
const routes: RouteRecordRaw[] = [
|
||
|
|
{
|
||
|
|
path: '/login',
|
||
|
|
name: 'Login',
|
||
|
|
component: () => import('@/views/auth/Login.vue'),
|
||
|
|
meta: { requiresAuth: false }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
path: '/',
|
||
|
|
component: () => import('@/components/layout/MainLayout.vue'),
|
||
|
|
meta: { requiresAuth: true },
|
||
|
|
children: [
|
||
|
|
{
|
||
|
|
path: '',
|
||
|
|
name: 'Home',
|
||
|
|
component: () => import('@/views/home/index.vue')
|
||
|
|
},
|
||
|
|
{
|
||
|
|
path: 'groups',
|
||
|
|
name: 'GroupList',
|
||
|
|
component: () => import('@/views/group/List.vue')
|
||
|
|
},
|
||
|
|
{
|
||
|
|
path: 'groups/:id',
|
||
|
|
name: 'GroupDetail',
|
||
|
|
component: () => import('@/views/group/Detail.vue')
|
||
|
|
},
|
||
|
|
// 更多路由...
|
||
|
|
]
|
||
|
|
}
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7.2 路由守卫
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// router/guards.ts
|
||
|
|
router.beforeEach((to, from, next) => {
|
||
|
|
const authStore = useAuthStore()
|
||
|
|
const requiresAuth = to.meta.requiresAuth !== false
|
||
|
|
|
||
|
|
if (requiresAuth && !authStore.isLoggedIn) {
|
||
|
|
// 未登录,跳转到登录页
|
||
|
|
next({
|
||
|
|
name: 'Login',
|
||
|
|
query: { redirect: to.fullPath }
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
next()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. API请求层
|
||
|
|
|
||
|
|
### 8.1 Axios封装
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// api/index.ts
|
||
|
|
import axios from 'axios'
|
||
|
|
import { ElMessage } from 'element-plus'
|
||
|
|
|
||
|
|
const service = axios.create({
|
||
|
|
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||
|
|
timeout: 15000
|
||
|
|
})
|
||
|
|
|
||
|
|
// 请求拦截器
|
||
|
|
service.interceptors.request.use(
|
||
|
|
(config) => {
|
||
|
|
const authStore = useAuthStore()
|
||
|
|
if (authStore.token) {
|
||
|
|
config.headers.Authorization = `Bearer ${authStore.token}`
|
||
|
|
}
|
||
|
|
return config
|
||
|
|
},
|
||
|
|
(error) => {
|
||
|
|
return Promise.reject(error)
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
// 响应拦截器
|
||
|
|
service.interceptors.response.use(
|
||
|
|
(response) => {
|
||
|
|
const { code, message, data } = response.data
|
||
|
|
|
||
|
|
if (code === 0) {
|
||
|
|
return data
|
||
|
|
} else {
|
||
|
|
ElMessage.error(message || '请求失败')
|
||
|
|
return Promise.reject(new Error(message))
|
||
|
|
}
|
||
|
|
},
|
||
|
|
(error) => {
|
||
|
|
if (error.response?.status === 401) {
|
||
|
|
// Token过期,刷新Token或跳转登录
|
||
|
|
const authStore = useAuthStore()
|
||
|
|
authStore.logout()
|
||
|
|
}
|
||
|
|
return Promise.reject(error)
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
export default service
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.2 API模块化
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// api/user.ts
|
||
|
|
import request from './index'
|
||
|
|
|
||
|
|
export const userApi = {
|
||
|
|
// 获取用户信息
|
||
|
|
getProfile() {
|
||
|
|
return request.get<UserInfo>('/users/me')
|
||
|
|
},
|
||
|
|
|
||
|
|
// 更新用户信息
|
||
|
|
updateProfile(data: UpdateProfileDto) {
|
||
|
|
return request.put<UserInfo>('/users/me', data)
|
||
|
|
},
|
||
|
|
|
||
|
|
// 修改密码
|
||
|
|
changePassword(data: ChangePasswordDto) {
|
||
|
|
return request.put('/users/me/password', data)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. 性能优化
|
||
|
|
|
||
|
|
### 9.1 代码分割
|
||
|
|
|
||
|
|
#### 路由级代码分割
|
||
|
|
使用动态import实现路由懒加载:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
{
|
||
|
|
path: '/groups',
|
||
|
|
component: () => import('@/views/group/List.vue')
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 组件级代码分割
|
||
|
|
```vue
|
||
|
|
<script setup>
|
||
|
|
import { defineAsyncComponent } from 'vue'
|
||
|
|
|
||
|
|
const HeavyComponent = defineAsyncComponent(() =>
|
||
|
|
import('./HeavyComponent.vue')
|
||
|
|
)
|
||
|
|
</script>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.2 资源优化
|
||
|
|
|
||
|
|
#### 图片优化
|
||
|
|
- 使用WebP格式
|
||
|
|
- 实现懒加载
|
||
|
|
- 响应式图片
|
||
|
|
|
||
|
|
#### 依赖优化
|
||
|
|
- Tree-shaking
|
||
|
|
- 按需引入组件库
|
||
|
|
- 使用轻量级替代方案
|
||
|
|
|
||
|
|
### 9.3 渲染优化
|
||
|
|
|
||
|
|
#### 虚拟滚动
|
||
|
|
长列表使用虚拟滚动:
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<template>
|
||
|
|
<VirtualList
|
||
|
|
:items="largeList"
|
||
|
|
:item-height="50"
|
||
|
|
:visible-height="600"
|
||
|
|
/>
|
||
|
|
</template>
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 防抖与节流
|
||
|
|
```typescript
|
||
|
|
import { useDebounceFn } from '@vueuse/core'
|
||
|
|
|
||
|
|
const search = useDebounceFn((keyword: string) => {
|
||
|
|
// 执行搜索
|
||
|
|
}, 300)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.4 缓存策略
|
||
|
|
|
||
|
|
#### 接口缓存
|
||
|
|
```typescript
|
||
|
|
// api/cache.ts
|
||
|
|
const cache = new Map()
|
||
|
|
|
||
|
|
export async function cachedFetch<T>(
|
||
|
|
key: string,
|
||
|
|
fetcher: () => Promise<T>,
|
||
|
|
ttl = 60000
|
||
|
|
): Promise<T> {
|
||
|
|
const cached = cache.get(key)
|
||
|
|
if (cached && Date.now() - cached.time < ttl) {
|
||
|
|
return cached.data
|
||
|
|
}
|
||
|
|
|
||
|
|
const data = await fetcher()
|
||
|
|
cache.set(key, { data, time: Date.now() })
|
||
|
|
return data
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 组件缓存
|
||
|
|
```vue
|
||
|
|
<KeepAlive :include="['GroupList', 'GameList']">
|
||
|
|
<RouterView />
|
||
|
|
</KeepAlive>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. 开发工具链
|
||
|
|
|
||
|
|
### 10.1 代码质量
|
||
|
|
|
||
|
|
- **ESLint**: 代码规范检查
|
||
|
|
- **Prettier**: 代码格式化
|
||
|
|
- **Stylelint**: 样式规范检查
|
||
|
|
- **TypeScript**: 类型检查
|
||
|
|
|
||
|
|
### 10.2 Git规范
|
||
|
|
|
||
|
|
- **Husky**: Git hooks
|
||
|
|
- **lint-staged**: 暂存文件检查
|
||
|
|
- **Commitlint**: 提交信息规范
|
||
|
|
|
||
|
|
### 10.3 CI/CD
|
||
|
|
|
||
|
|
- **GitHub Actions**: 自动化部署
|
||
|
|
- **Vercel/Netlify**: 前端托管
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. 测试策略
|
||
|
|
|
||
|
|
### 11.1 单元测试
|
||
|
|
|
||
|
|
使用Vitest进行单元测试:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// tests/utils/format.test.ts
|
||
|
|
import { describe, it, expect } from 'vitest'
|
||
|
|
import { formatDate } from '@/utils/format'
|
||
|
|
|
||
|
|
describe('formatDate', () => {
|
||
|
|
it('should format date correctly', () => {
|
||
|
|
const date = new Date('2026-01-28')
|
||
|
|
expect(formatDate(date)).toBe('2026-01-28')
|
||
|
|
})
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### 11.2 组件测试
|
||
|
|
|
||
|
|
使用Vue Test Utils:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { mount } from '@vue/test-utils'
|
||
|
|
import Button from '@/components/common/Button/Button.vue'
|
||
|
|
|
||
|
|
describe('Button', () => {
|
||
|
|
it('renders text', () => {
|
||
|
|
const wrapper = mount(Button, {
|
||
|
|
slots: { default: 'Click me' }
|
||
|
|
})
|
||
|
|
expect(wrapper.text()).toBe('Click me')
|
||
|
|
})
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12. 部署方案
|
||
|
|
|
||
|
|
### 12.1 构建配置
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// vite.config.ts
|
||
|
|
export default defineConfig({
|
||
|
|
build: {
|
||
|
|
target: 'es2015',
|
||
|
|
outDir: 'dist',
|
||
|
|
assetsDir: 'assets',
|
||
|
|
sourcemap: false,
|
||
|
|
minify: 'terser',
|
||
|
|
terserOptions: {
|
||
|
|
compress: {
|
||
|
|
drop_console: true,
|
||
|
|
drop_debugger: true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
rollupOptions: {
|
||
|
|
output: {
|
||
|
|
manualChunks: {
|
||
|
|
'vendor': ['vue', 'vue-router', 'pinia'],
|
||
|
|
'ui': ['element-plus']
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### 12.2 环境变量
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# .env.development
|
||
|
|
VITE_API_BASE_URL=http://localhost:3000/api
|
||
|
|
|
||
|
|
# .env.production
|
||
|
|
VITE_API_BASE_URL=https://api.gamegroup.com/api
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 13. 总结
|
||
|
|
|
||
|
|
本架构设计遵循以下原则:
|
||
|
|
- ✅ 模块化: 清晰的模块划分,职责明确
|
||
|
|
- ✅ 可维护性: 规范的代码组织,易于维护
|
||
|
|
- ✅ 可扩展性: 灵活的架构设计,便于扩展
|
||
|
|
- ✅ 性能优化: 多层次的优化策略
|
||
|
|
- ✅ 开发体验: 完善的工具链支持
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**文档维护**: 本文档应随项目演进持续更新
|
||
|
|
**最后更新**: 2026-01-28
|