Files
gamegroup_fe/doc/design/01-前端架构设计.md
2026-01-28 14:35:43 +08:00

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