Files
36-character-flower/README.md
2026-04-23 16:41:39 +08:00

421 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# React SPA Template
这是一个用于后台系统或通用 SPA 项目的前端基础脚手架。它已经接好了以下底座能力:
- `Vite + React 19 + TypeScript`
- `TanStack Router` 文件路由
- `TanStack Query + ky` 请求与缓存
- `Zustand` 会话状态基座
- `Tailwind CSS` 原子化样式
- `i18next` 多语言
- `Head / Metadata` 动态标题与元信息
- `Biome + Husky + commitlint` 代码规范与提交规范
这个模板现在已经被“净化”为一个空白 scaffold。
你拿到它之后,应该在这个基础上替换成自己的页面、接口、登录逻辑和测试,而不是继续沿用示例业务。
## 1. 快速开始
```bash
pnpm install
pnpm dev
```
常用命令:
```bash
pnpm dev
pnpm build
pnpm lint
pnpm lint:fix
pnpm generate-routes
pnpm commit
```
## 2. 目录结构
核心目录说明:
- `src/routes`
TanStack Router 文件路由目录。你新增页面,优先在这里建文件。
- `src/lib/api`
通用请求层。`api-client.ts` 是所有接口调用的基础入口。
- `src/lib/auth`
会话初始化、401 清理、refresh token、受保护路由 helper。
- `src/lib/head`
页面标题、description、Open Graph、Twitter metadata。
- `src/lib/query`
TanStack Query 全局默认配置。
- `src/store`
全局状态。目前主要是认证状态。
- `src/styles.css`
全局 Tailwind 入口。当前项目只保留这一份样式文件。
- `src/locales`
多语言文案。
## 3. 新项目落地后先改哪里
如果你要基于这个模板开一个新项目,建议按这个顺序改:
### 第一步:改首页
先改这个文件:
- `src/routes/$lang/index.tsx`
这是当前默认首页。通常你会:
- 改成你的项目欢迎页
- 或直接改成 dashboard / 工作台入口
- 或拆成自己的页面布局
同时对应修改多语言文案:
- `src/locales/zh-CN/common.ts`
- `src/locales/en-US/common.ts`
### 第二步:改接口基础地址
先看这些文件:
- `.env.development`
- `.env.production`
- `src/vite-env.d.ts`
至少确认这些变量:
- `VITE_APP_ENV`
- `VITE_API_BASE_URL`
- `VITE_ENABLE_QUERY_DEVTOOLS`
- `VITE_ENABLE_REQUEST_LOG`
### 第三步:改全局样式入口
当前模板已经切到 Tailwind CSS并移除了旧的 `App.css` / `index.css`
你主要会改:
- `src/styles.css`
这里通常只放:
- `@import "tailwindcss";`
- 少量全局 base 样式
- 你的主题变量
### 第四步:接你的业务模块
建议按“功能模块”建目录,而不是把所有请求、类型和 hooks 混在一起。
例如:
```text
src/features/user/api/user-api.ts
src/features/user/hooks/use-current-user.ts
src/features/user/types/user.ts
src/routes/$lang/users/index.tsx
```
### 第五步:接你的登录体系
优先改这些文件:
- `src/store/auth-store.ts`
- `src/lib/auth/auth-session.ts`
- `src/lib/api/api-client.ts`
## 4. Head / Metadata 怎么用
模板已经提供了一个通用 hook
- `src/lib/head/document-metadata.ts`
在页面组件里直接调用:
```tsx
import { useDocumentMetadata } from '@/lib/head/document-metadata'
function UserPage() {
useDocumentMetadata({
title: '用户管理',
description: '用户管理页面',
})
return <div>User Page</div>
}
```
它会自动更新:
- `document.title`
- `meta[name="description"]`
- `meta[name="robots"]`
- `og:title`
- `og:description`
- `twitter:title`
- `twitter:description`
适用场景:
- 列表页标题
- 详情页标题
- 登录页 / 404 页
- 分享卡片基础描述
## 5. 登录怎么接
这个模板的认证层不是“现成登录系统”,而是“认证骨架”。
它已经有:
- `accessToken`
- `refreshToken`
- `currentUser`
- `status`
- `401` 后清会话
- refresh token 的可插拔入口
核心文件:
- `src/store/auth-store.ts`
- `src/lib/auth/auth-session.ts`
- `src/lib/api/api-client.ts`
### 5.1 登录成功后怎么写入会话
假设你的登录接口返回:
```ts
{
accessToken: string
refreshToken: string
user: {
id: string
name: string
}
}
```
那么登录成功后可以这样写:
```ts
import { useAuthStore } from '@/store/auth-store'
useAuthStore.getState().startSession({
accessToken: response.accessToken,
refreshToken: response.refreshToken,
currentUser: response.user,
})
```
### 5.2 刷新 token 怎么接
模板里预留了 refresh 的注册入口:
```ts
import { registerRefreshSessionHandler } from '@/lib/auth/auth-session'
registerRefreshSessionHandler(async (refreshToken) => {
const response = await fetch('/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ refreshToken }),
}).then((result) => result.json())
return {
accessToken: response.accessToken,
refreshToken: response.refreshToken,
currentUser: response.user ?? null,
}
})
```
当请求返回 `401` 时,请求层会:
1. 尝试执行这个 refresh handler
2. 如果 refresh 成功,则自动重试原请求一次
3. 如果 refresh 失败,则清空本地会话
### 5.3 当前用户初始化怎么接
如果你的项目在刷新页面后,需要根据已有 token 主动请求一次“当前用户信息”,可以注册:
```ts
import { registerCurrentUserInitializer } from '@/lib/auth/auth-session'
registerCurrentUserInitializer(async () => {
const response = await fetch('/auth/me').then((result) => result.json())
return response.user
})
```
这样在应用启动时,模板会在已有 token 且尚未拿到 `currentUser` 的情况下,自动初始化一次用户信息。
## 6. 怎么加受保护路由
模板已经提供了路由保护 helper
- `src/lib/auth/require-auth.ts`
用法是,在路由的 `beforeLoad` 里调用它。
示例:
```tsx
import { createFileRoute } from '@tanstack/react-router'
import { requireAuthenticatedSession } from '@/lib/auth/require-auth'
export const Route = createFileRoute('/$lang/dashboard')({
beforeLoad: async () => {
await requireAuthenticatedSession()
},
component: DashboardPage,
})
function DashboardPage() {
return <div>Dashboard</div>
}
```
它的行为是:
- 如果当前已经登录,正常进入页面
- 如果当前未登录,重定向到 `/$lang`
如果你想把未登录用户导向专门的登录页,可以直接修改:
- `src/lib/auth/require-auth.ts`
把重定向目标从 `/$lang` 改成你的登录页路由,例如 `/$lang/login`
## 7. 怎么写第一个接口模块
推荐模式是:
1.`src/features/.../types` 定义类型
2.`src/features/.../api` 写请求函数
3.`src/features/.../hooks` 写 query / mutation hook
4.`src/routes/...` 页面里消费
示例:
```ts
// src/features/user/api/user-api.ts
import { api } from '@/lib/api/api-client'
export interface CurrentUser {
id: string
name: string
}
export function getCurrentUser() {
return api.get<CurrentUser>('users/me').then((response) => response.data)
}
```
```ts
// src/features/user/hooks/use-current-user.ts
import { useQuery } from '@tanstack/react-query'
import { getCurrentUser } from '@/features/user/api/user-api'
import type { ApiError } from '@/lib/api/api-error'
export function useCurrentUserQuery() {
return useQuery<Awaited<ReturnType<typeof getCurrentUser>>, ApiError>({
queryKey: ['current-user'],
queryFn: getCurrentUser,
})
}
```
## 8. 路由相关说明
这个模板使用 TanStack Router 文件路由。
路由目录:
- `src/routes`
生成文件:
- `src/routeTree.gen.ts`
你通常不需要手改 `src/routeTree.gen.ts`
新增、删除、修改路由文件后,执行:
```bash
pnpm generate-routes
```
`pnpm dev``pnpm build` 也都会自动先生成一次路由树。
## 9. 代码规范和提交流程
## 9.1 Tailwind 说明
当前模板使用的是官方 Vite 接法:
- `tailwindcss`
- `@tailwindcss/vite`
核心文件:
- `vite.config.ts`
- `src/styles.css`
新增页面时,优先直接写 Tailwind utility class而不是再恢复成多个分散的 CSS 文件。
格式化与 lint
```bash
pnpm lint
pnpm lint:fix
pnpm format
```
提交规范:
```bash
pnpm commit
```
仓库已经包含:
- `Biome`
- `Husky`
- `lint-staged`
- `commitlint`
- `Commitizen`
## 10. 你后续最可能改动的文件
如果你刚接手这个模板,优先关注这几个文件:
- `src/routes/$lang/index.tsx`
- `src/routes/$lang/route.tsx`
- `src/lib/api/api-client.ts`
- `src/lib/auth/auth-session.ts`
- `src/lib/auth/require-auth.ts`
- `src/store/auth-store.ts`
- `src/lib/head/document-metadata.ts`
- `src/locales/zh-CN/common.ts`
- `src/locales/en-US/common.ts`
## 11. 当前状态
当前模板已经验证通过:
- `pnpm lint`
- `pnpm build`
如果你继续往前整理,下一步通常就是:
1. 接入真实登录接口
2. 增加 `login``dashboard``403` 等实际页面
3. 按业务模块拆分 `features`
4. 按项目实际需要补充测试方案