|
@@ -0,0 +1,408 @@
|
|
|
|
|
+# 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 | 图标系统 | 简洁一致的图标设计 |
|
|
|
|
|
+| 测试框架 | Jest + Testing Library | - | 组件测试 | 行业标准测试工具 |
|
|
|
|
|
+
|
|
|
|
|
+## 3. 项目结构
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+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
|
|
|
|
|
+│ │ │ │ └── ...
|
|
|
|
|
+│ │ │ ├── forms/ # 表单组件
|
|
|
|
|
+│ │ │ ├── layout/ # 布局组件
|
|
|
|
|
+│ │ │ └── shared/ # 业务共享组件
|
|
|
|
|
+│ │ ├── hooks/ # 共享React Hooks
|
|
|
|
|
+│ │ │ ├── use-api.ts # API调用hook
|
|
|
|
|
+│ │ │ ├── use-auth.ts # 认证hook
|
|
|
|
|
+│ │ │ └── use-query.ts # React Query封装
|
|
|
|
|
+│ │ ├── lib/ # 工具库
|
|
|
|
|
+│ │ │ ├── utils.ts # 通用工具函数
|
|
|
|
|
+│ │ │ ├── cn.ts # className工具
|
|
|
|
|
+│ │ │ └── validators.ts # 表单验证器
|
|
|
|
|
+│ │ ├── types/ # 前端类型定义
|
|
|
|
|
+│ │ │ ├── api.ts # API响应类型
|
|
|
|
|
+│ │ │ ├── forms.ts # 表单数据类型
|
|
|
|
|
+│ │ │ └── index.ts # 类型导出
|
|
|
|
|
+│ │ ├── styles/ # 样式文件
|
|
|
|
|
+│ │ │ ├── globals.css # 全局样式
|
|
|
|
|
+│ │ │ └── components.css # 组件样式
|
|
|
|
|
+│ │ └── utils/ # 工具函数
|
|
|
|
|
+│ │ ├── api.ts # API客户端配置
|
|
|
|
|
+│ │ ├── error-handler.ts # 错误处理
|
|
|
|
|
+│ │ └── constants.ts # 常量定义
|
|
|
|
|
+│ ├── server/ # 后端代码
|
|
|
|
|
+│ └── shared/ # 前后端共享代码
|
|
|
|
|
+├── public/ # 静态资源
|
|
|
|
|
+├── dist/ # 构建输出
|
|
|
|
|
+└── docs/ # 文档
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 4. 组件标准
|
|
|
|
|
+
|
|
|
|
|
+### 组件模板
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+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. 状态管理
|
|
|
|
|
+
|
|
|
|
|
+### Store结构
|
|
|
|
|
+
|
|
|
|
|
+```text
|
|
|
|
|
+src/
|
|
|
|
|
+├── client/
|
|
|
|
|
+│ ├── hooks/
|
|
|
|
|
+│ │ ├── use-api.ts # API调用封装
|
|
|
|
|
+│ │ ├── use-auth.ts # 认证状态
|
|
|
|
|
+│ │ ├── use-users.ts # 用户数据
|
|
|
|
|
+│ │ ├── use-roles.ts # 角色数据
|
|
|
|
|
+│ │ └── index.ts # hooks导出
|
|
|
|
|
+│ ├── lib/
|
|
|
|
|
+│ │ ├── query-client.ts # React Query配置
|
|
|
|
|
+│ │ └── query-keys.ts # 查询键常量
|
|
|
|
|
+│ └── providers/
|
|
|
|
|
+│ └── query-provider.tsx # QueryClientProvider
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### React Query配置
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+export const queryClient = new QueryClient({
|
|
|
|
|
+ defaultOptions: {
|
|
|
|
|
+ queries: {
|
|
|
|
|
+ staleTime: 5 * 60 * 1000, // 5分钟
|
|
|
|
|
+ gcTime: 10 * 60 * 1000, // 10分钟
|
|
|
|
|
+ retry: 1,
|
|
|
|
|
+ refetchOnWindowFocus: false,
|
|
|
|
|
+ },
|
|
|
|
|
+ mutations: {
|
|
|
|
|
+ retry: 1,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 6. API集成
|
|
|
|
|
+
|
|
|
|
|
+### Hono RPC集成模式
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 创建axios适配器用于Hono RPC
|
|
|
|
|
+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;
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return new Response(
|
|
|
|
|
+ JSON.stringify(response.data),
|
|
|
|
|
+ {
|
|
|
|
|
+ status: response.status,
|
|
|
|
|
+ statusText: response.statusText,
|
|
|
|
|
+ headers: response.headers
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Hono客户端实例
|
|
|
|
|
+export const userClient = hc<UserRoutes>('/', {
|
|
|
|
|
+ fetch: axiosFetch,
|
|
|
|
|
+}).api.v1.users;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 7. 路由配置
|
|
|
|
|
+
|
|
|
|
|
+### 当前路由架构
|
|
|
|
|
+
|
|
|
|
|
+**双路由系统**:
|
|
|
|
|
+- 管理后台路由: `/admin/*` (位于 `src/client/admin/routes.tsx`)
|
|
|
|
|
+- 用户前台路由: `/*` (位于 `src/client/home/routes.tsx`)
|
|
|
|
|
+
|
|
|
|
|
+**路由结构**:
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 管理后台路由
|
|
|
|
|
+- /admin/login → 登录页面
|
|
|
|
|
+- /admin → 重定向到仪表板 (保护路由)
|
|
|
|
|
+- /admin/dashboard → 仪表板页面 (保护路由)
|
|
|
|
|
+- /admin/users → 用户管理页面 (保护路由)
|
|
|
|
|
+
|
|
|
|
|
+// 用户前台路由
|
|
|
|
|
+- / → 首页
|
|
|
|
|
+- /login → 登录页面
|
|
|
|
|
+- /register → 注册页面
|
|
|
|
|
+- /member → 会员页面 (保护路由)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 认证保护
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+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自定义属性的主题系统:
|
|
|
|
|
+
|
|
|
|
|
+```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. 测试要求
|
|
|
|
|
+
|
|
|
|
|
+### 测试策略
|
|
|
|
|
+
|
|
|
|
|
+**测试框架**: Jest + Testing Library
|
|
|
|
|
+**测试位置**: `__tests__`文件夹与源码并列
|
|
|
|
|
+**覆盖率目标**: 核心组件80%+覆盖率
|
|
|
|
|
+
|
|
|
|
|
+### 测试模板
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+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 = jest.fn()
|
|
|
|
|
+ render(<Button onClick={handleClick}>Click me</Button>)
|
|
|
|
|
+
|
|
|
|
|
+ fireEvent.click(screen.getByRole('button'))
|
|
|
|
|
+ expect(handleClick).toHaveBeenCalledTimes(1)
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 10. 环境配置
|
|
|
|
|
+
|
|
|
|
|
+### 环境变量
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+VITE_API_BASE_URL=http://localhost:8080
|
|
|
|
|
+VITE_APP_NAME=D8D Starter
|
|
|
|
|
+VITE_ENABLE_ANALYTICS=false
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 11. 前端开发标准
|
|
|
|
|
+
|
|
|
|
|
+### 关键编码规则
|
|
|
|
|
+
|
|
|
|
|
+1. **类型安全**: 全面使用TypeScript,避免any类型
|
|
|
|
|
+2. **组件设计**: 遵循单一职责原则
|
|
|
|
|
+3. **性能优化**: 使用React.memo、useCallback等优化手段
|
|
|
|
|
+4. **错误处理**: 统一的错误边界和异常处理
|
|
|
|
|
+5. **可访问性**: 支持ARIA属性和键盘导航
|
|
|
|
|
+
|
|
|
|
|
+### 快速参考
|
|
|
|
|
+
|
|
|
|
|
+**常用命令**:
|
|
|
|
|
+```bash
|
|
|
|
|
+npm run dev # 启动开发服务器
|
|
|
|
|
+npm run build # 生产构建
|
|
|
|
|
+npm run test # 运行测试
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+**文档状态**: 正式版
|
|
|
|
|
+**下次评审**: 2025-10-15
|
|
|
|
|
+**架构师**: Winston 🏗️
|