17 KiB
17 KiB
GameGroup 前端架构设计文档
项目名称: GameGroup 前端系统 文档版本: v1.0 更新时间: 2026-01-28
📋 目录
1. 项目概述
GameGroup 是一个游戏社群管理平台,为玩家提供小组管理、游戏预约、积分系统、竞猜等功能。前端项目采用现代化的技术栈,注重用户体验和性能优化。
核心功能模块
- 认证模块: 用户注册、登录、令牌刷新
- 用户中心: 个人资料管理、设置
- 小组管理: 创建/加入小组、成员管理、权限控制
- 游戏库: 游戏浏览、搜索、筛选
- 预约系统: 创建预约、加入/退出预约、时间管理
- 账目管理: 账目记录、统计分析
- 排班助手: 空闲时间提交、共同时间查找
- 荣誉墙: 荣誉展示、时间轴
- 资产管理: 资产借用/归还、流转记录
- 积分系统: 积分查询、排行榜
- 竞猜系统: 创建竞猜、参与竞猜、结算
2. 技术栈选型
核心框架
{
"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统一导出
注释规范
/**
* 获取用户信息
* @param userId - 用户ID
* @returns 用户信息对象
*/
async function getUserInfo(userId: string): Promise<User> {
// 实现
}
6. 状态管理
6.1 Pinia Store设计
模块化Store
每个业务模块创建独立的Store:
// 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
应用级别的状态:
// 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实现状态持久化:
// pinia.ts
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export default pinia
7. 路由设计
7.1 路由结构
// 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 路由守卫
// 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封装
// 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模块化
// 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实现路由懒加载:
{
path: '/groups',
component: () => import('@/views/group/List.vue')
}
组件级代码分割
<script setup>
import { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
</script>
9.2 资源优化
图片优化
- 使用WebP格式
- 实现懒加载
- 响应式图片
依赖优化
- Tree-shaking
- 按需引入组件库
- 使用轻量级替代方案
9.3 渲染优化
虚拟滚动
长列表使用虚拟滚动:
<template>
<VirtualList
:items="largeList"
:item-height="50"
:visible-height="600"
/>
</template>
防抖与节流
import { useDebounceFn } from '@vueuse/core'
const search = useDebounceFn((keyword: string) => {
// 执行搜索
}, 300)
9.4 缓存策略
接口缓存
// 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
}
组件缓存
<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进行单元测试:
// 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:
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 构建配置
// 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 环境变量
# .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