2
0

ui-architecture.md 14 KB

D8D Starter 前端架构文档

版本信息

版本 日期 描述 作者
1.0 2025-09-15 初始前端架构文档 Winston

1. 模板和框架选择

现有技术栈分析

项目已经使用了现代化的React全栈技术栈:

核心框架:

  • React 19.1.0 - 最新版本,支持并发特性
  • TypeScript 5.8.3 - 类型安全,编译时错误检测

构建工具:

  • Vite 7.0.0 - 快速冷启动,热重载支持
  • Tailwind CSS 4.1.11 - 原子化CSS,实用优先

UI组件库:

  • shadcn/ui - 基于Radix UI的无障碍组件库
  • Radix UI Primitives - 无障碍基础组件

状态管理:

  • React Query 5.83.0 - 服务端状态管理,数据同步
  • React Hook Form 7.61.1 - 表单管理和验证

路由系统:

  • React Router 7.7.0 - 声明式客户端路由
  • 双路由架构 - 管理后台(/admin/)和用户前台(/)分离

API集成:

  • Hono RPC 4.8.5 - 端到端类型安全API调用
  • 自定义axios适配器 - 统一的错误处理

技术决策依据

  • 选择React Query而非Redux: 专注于服务端状态管理,减少客户端状态复杂度
  • Hono RPC而非传统REST: 提供前后端统一的类型安全,减少接口不一致问题
  • Tailwind CSS v4: 采用新的运行时配置,简化构建流程
  • shadcn/ui而非Ant Design: 更现代的设计语言,更好的无障碍支持

2. 前端技术栈

类别 技术 版本 用途 决策依据
框架 React 19.1.0 用户界面构建 最新版本,并发特性支持
UI库 shadcn/ui - 组件库和设计系统 基于Radix UI,无障碍支持
状态管理 React Query 5.83.0 服务端状态管理 数据同步、缓存、自动重试
路由 React Router 7.7.0 客户端路由 声明式路由,数据加载支持
构建工具 Vite 7.0.0 开发服务器和构建 快速冷启动,热重载支持
样式方案 Tailwind CSS 4.1.11 原子化CSS框架 实用优先,设计一致性
类型安全 TypeScript 5.8.3 类型检查 编译时错误检测
API客户端 Hono RPC 4.8.5 类型安全API调用 前后端统一类型定义
表单处理 React Hook Form 7.61.1 表单管理和验证 高性能,最小重渲染
图标库 Lucide React 0.536.0 图标系统 简洁一致的图标设计
测试框架 Vitest + Testing Library - 组件测试 现代化测试工具,更好的TypeORM支持

3. 项目结构

d8d-starter/
├── src/
│   ├── client/                 # 前端代码根目录
│   │   ├── admin/              # 管理后台界面
│   │   │   ├── components/     # 管理后台专属组件
│   │   │   ├── pages/          # 管理页面
│   │   │   ├── layouts/        # 管理后台布局
│   │   │   └── hooks/          # 管理后台专属hooks
│   │   ├── home/               # 用户主页界面
│   │   │   ├── components/     # 主页专属组件
│   │   │   ├── pages/          # 主页页面
│   │   │   └── hooks/          # 主页专属hooks
│   │   ├── components/         # 共享UI组件
│   │   │   └── ui/             # 基础UI组件 (shadcn/ui)
│   │   │       ├── button.tsx
│   │   │       ├── input.tsx
│   │   │       └── ...
│   │   ├── hooks/              # 共享React Hooks
│   │   │   └── use-mobile.ts   # 移动端检测hook
│   │   ├── lib/                # 工具库
│   │   │   └── utils.ts        # className工具函数 (cn)
│   │   ├── utils/              # 工具函数目录
│   │   │   ├── utils.ts        # 通用工具函数
│   │   │   ├── logger.ts       # 日志工具
│   │   │   └── ClientOnly.tsx  # 客户端渲染组件
│   │   └── api.ts              # API客户端配置
│   ├── server/                 # 后端代码
│   └── shared/                 # 前后端共享代码
├── public/                     # 静态资源
├── dist/                       # 构建输出
└── docs/                       # 文档

4. 组件标准

组件模板

import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/client/lib/utils"

const componentVariants = cva(
  "base-styles transition-all duration-200",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground",
        secondary: "bg-secondary text-secondary-foreground",
        destructive: "bg-destructive text-destructive-foreground",
        outline: "border border-input bg-background",
        ghost: "hover:bg-accent hover:text-accent-foreground",
      },
      size: {
        default: "h-9 px-4 py-2",
        sm: "h-8 rounded-md px-3 text-xs",
        lg: "h-10 rounded-md px-8",
        icon: "size-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ComponentProps
  extends React.HTMLAttributes<HTMLDivElement>,
    VariantProps<typeof componentVariants> {
  isLoading?: boolean
  disabled?: boolean
}

const Component = React.forwardRef<HTMLDivElement, ComponentProps>(
  ({ className, variant, size, isLoading = false, disabled = false, ...props }, ref) => {
    return (
      <div
        ref={ref}
        className={cn(
          componentVariants({ variant, size, className }),
          isLoading && "opacity-50 cursor-not-allowed",
          disabled && "pointer-events-none opacity-50"
        )}
        aria-disabled={disabled || isLoading}
        {...props}
      />
    )
  }
)
Component.displayName = "Component"

export { Component, componentVariants }

命名约定

文件命名:

  • 组件文件: PascalCase.tsx (如: UserProfile.tsx)
  • 工具文件: camelCase.ts (如: formatDate.ts)
  • Hook文件: useCamelCase.ts (如: useUserData.ts)
  • 类型文件: camelCase.ts (如: apiTypes.ts)

组件命名:

  • 组件: PascalCase (如: UserCard)
  • Props接口: ComponentProps (如: ButtonProps)
  • 变体类型: ComponentVariants (如: ButtonVariants)

5. 状态管理

实际状态管理架构

基于对现有代码的分析,项目采用混合状态管理模式:

React Context + React Query组合:

  • 认证状态: 使用React Context管理用户认证状态
  • 服务端数据: 使用React Query管理API数据
  • 本地UI状态: 使用useState管理组件内部状态

认证状态管理 (React Context)

src/
├── client/
│   ├── admin/
│   │   └── hooks/
│   │       └── AuthProvider.tsx    # 管理后台认证上下文
│   ├── home/
│   │   └── hooks/
│   │       └── AuthProvider.tsx    # 用户前台认证上下文
│   ├── hooks/
│   │   └── use-mobile.ts           # 共享移动端检测hook
│   └── api.ts                      # API客户端配置

React Query使用模式

项目采用简单的React Query使用模式:

配置方式: 在每个入口文件独立创建QueryClient

// 在入口文件中创建QueryClient
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 1,
      refetchOnWindowFocus: false,
    },
  },
})

查询模式: 直接在组件中使用useQuery

// 数据查询示例
const { data: usersData, isLoading, refetch } = useQuery({
  queryKey: ['users', searchParams],
  queryFn: async () => {
    const res = await userClient.$get({ query: searchParams })
    if (res.status !== 200) throw new Error('获取失败')
    return await res.json()
  }
})

数据变更模式: 直接调用API客户端 + 手动refetch

// 数据变更示例(未使用useMutation)
const handleCreateSubmit = async (data: CreateUserFormData) => {
  try {
    const res = await userClient.$post({ json: data })
    if (res.status !== 201) throw new Error('创建失败')
    toast.success('创建成功')
    refetch() // 手动重新获取数据
  } catch (error) {
    console.error('操作失败:', error)
    toast.error('操作失败,请重试')
  }
}

当前状态管理特点

  1. 模块化认证: 管理后台和用户前台有独立的认证Provider
  2. 简单配置: QueryClient在每个入口文件独立配置,无复杂全局配置
  3. 混合模式: Context管理认证状态 + React Query管理数据查询
  4. 手动数据同步: 数据变更后手动调用refetch更新缓存
  5. 渐进式采用: 逐步引入React Query,尚未全面使用useMutation

6. API集成

Hono RPC集成模式

项目使用自定义的axios适配器实现Hono RPC客户端:

// 自定义axios适配器 (src/client/api.ts)
const axiosFetch = async (url: RequestInfo | URL, init?: RequestInit) => {
  const requestHeaders: Record<string, string> = {};

  if (init?.headers instanceof Headers) {
    init.headers.forEach((value, key) => {
      requestHeaders[key] = value;
    })
  }

  const response = await axios.request({
    url: url.toString(),
    method: init?.method || 'GET',
    headers: requestHeaders,
    data: init?.body,
  }).catch((error) => {
    if (isAxiosError(error)) {
      return {
        status: error.response?.status,
        statusText: error.response?.statusText,
        data: error.response?.data,
        headers: error.response?.headers
      }
    }
    throw error;
  })

  const responseHeaders = new Headers();
  if (response.headers) {
    for (const [key, value] of Object.entries(response.headers)) {
      responseHeaders.set(key, value);
    }
  }

  // 处理204 No Content响应
  const body = response.status === 204
    ? null
    : responseHeaders.get('content-type')?.includes('application/json')
      ? JSON.stringify(response.data)
      : response.data;

  return new Response(
    body,
    {
      status: response.status,
      statusText: response.statusText,
      headers: responseHeaders
    }
  )
}

// Hono客户端实例导出
export const authClient = hc<AuthRoutes>('/', {
  fetch: axiosFetch,
}).api.v1.auth;

export const userClient = hc<UserRoutes>('/', {
  fetch: axiosFetch,
}).api.v1.users;

export const roleClient = hc<RoleRoutes>('/', {
  fetch: axiosFetch,
}).api.v1.roles;

类型安全集成

后端使用OpenAPIHono定义路由类型:

// 服务器端路由定义 (src/server/api.ts)
const userRoutes = api.route('/api/v1/users', usersRouter)
const authRoutes = api.route('/api/v1/auth', authRoute)
const roleRoutes = api.route('/api/v1/roles', rolesRoute)

export type AuthRoutes = typeof authRoutes
export type UserRoutes = typeof userRoutes
export type RoleRoutes = typeof roleRoutes

使用模式

在组件中直接使用类型安全的客户端:

// 使用Hono RPC客户端
const response = await userClient.$get({
  query: { page: 1, pageSize: 10, keyword: 'search' }
})

if (response.status === 200) {
  const data = await response.json()
  // 类型安全的data
}

7. 路由配置

当前路由架构

双路由系统:

  • 管理后台路由: /admin/* (位于 src/client/admin/routes.tsx)
  • 用户前台路由: /* (位于 src/client/home/routes.tsx)

路由结构:

// 管理后台路由
- /admin/login      → 登录页面
- /admin            → 重定向到仪表板 (保护路由)
- /admin/dashboard  → 仪表板页面 (保护路由)
- /admin/users      → 用户管理页面 (保护路由)

// 用户前台路由
- /          → 首页
- /login     → 登录页面
- /register  → 注册页面
- /member    → 会员页面 (保护路由)

认证保护

export const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
  const { isAuthenticated, isLoading } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    if (!isLoading && !isAuthenticated) {
      navigate('/admin/login', { replace: true });
    }
  }, [isAuthenticated, isLoading, navigate]);

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return null;
  }

  return children;
};

8. 样式指南

主题系统

基于CSS自定义属性的主题系统:

:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
  /* ... 更多设计token */
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --primary: oklch(0.985 0 0);
  /* ... 暗色主题变量 */
}

样式方法论

  • 原子化CSS: 使用Tailwind工具类构建UI
  • CSS变量: 通过CSS自定义属性实现主题切换
  • 设计系统: 基于shadcn/ui的组件设计规范
  • 响应式: 移动优先的响应式设计

9. 测试要求

测试策略

测试框架: Vitest + Testing Library 测试位置: __tests__文件夹与源码并列 覆盖率目标: 核心组件80%+覆盖率

测试模板

describe('Button', () => {
  it('renders correctly with default variant', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByRole('button')).toHaveTextContent('Click me')
  })

  it('calls onClick handler when clicked', () => {
    const handleClick = vi.fn()
    render(<Button onClick={handleClick}>Click me</Button>)

    fireEvent.click(screen.getByRole('button'))
    expect(handleClick).toHaveBeenCalledTimes(1)
  })
})

10. 环境配置

环境变量

# 实际环境变量配置(基于服务器端配置)
OSS_BASE_URL=https://oss.d8d.fun
APP_NAME=多八多Aider

11. 前端开发标准

关键编码规则

  1. 类型安全: 全面使用TypeScript,避免any类型
  2. 组件设计: 遵循单一职责原则
  3. 性能优化: 使用React.memo、useCallback等优化手段
  4. 错误处理: 统一的错误边界和异常处理
  5. 可访问性: 支持ARIA属性和键盘导航

快速参考

常用命令:

npm run dev      # 启动开发服务器
npm run build    # 生产构建
npm run test     # 运行测试

文档状态: 正式版 下次评审: 2025-10-15 架构师: Winston 🏗️