# 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
User Page
} ``` 它会自动更新: - `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
Dashboard
} ``` 它的行为是: - 如果当前已经登录,正常进入页面 - 如果当前未登录,重定向到 `/$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('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>, 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. 按项目实际需要补充测试方案