React 项目规范
name: react-project-standard
by bovinphang · published 2026-04-01
$ claw add gh:bovinphang/bovinphang-react-project-standard---
name: react-project-standard
description: React + TypeScript 项目的完整工程规范,涵盖项目结构、组件设计、Hooks、路由、状态管理、API 层、错误处理、测试和性能优化。当用户在 React 项目中创建、修改组件或模块,涉及架构设计、代码编写时自动激活。
version: 2.0.0
---
# React 项目规范
适用于使用 React + TypeScript 的仓库。
项目结构
以下为中大型 React 项目的业界最佳实践结构,按项目实际情况裁剪:
src/
├── app/ # 应用入口与全局配置
│ ├── App.tsx # 根组件(Provider 组合)
│ ├── routes.tsx # 路由配置
│ └── providers.tsx # 全局 Provider 组装
│
├── pages/ # 页面组件(与路由一一对应)
│ ├── Dashboard/
│ │ ├── DashboardPage.tsx
│ │ ├── components/ # 页面私有组件
│ │ └── hooks/ # 页面私有 hooks
│ ├── UserList/
│ └── Settings/
│
├── layouts/ # 布局组件
│ ├── MainLayout.tsx # 主布局(侧边栏 + 顶栏 + 内容区)
│ ├── AuthLayout.tsx # 登录/注册页布局
│ └── BlankLayout.tsx # 空白布局(错误页等)
│
├── features/ # 功能模块(按业务领域划分)
│ ├── auth/
│ │ ├── components/ # 模块组件
│ │ ├── hooks/ # 模块 hooks
│ │ ├── api.ts # 模块 API 调用
│ │ ├── types.ts # 模块类型定义
│ │ ├── constants.ts # 模块常量
│ │ └── index.ts # 模块公开导出
│ └── order/
│
├── components/ # 全局共享 UI 组件
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.module.css
│ │ └── __tests__/
│ ├── Modal/
│ ├── Form/
│ └── ErrorBoundary/
│
├── hooks/ # 全局共享 hooks
│ ├── useAuth.ts
│ ├── useDebounce.ts
│ └── useMediaQuery.ts
│
├── services/ # API 基础层
│ ├── request.ts # Axios/fetch 实例与拦截器
│ └── endpoints/ # API 端点定义(如按领域拆分)
│
├── stores/ # 全局状态管理
│ ├── authStore.ts
│ └── uiStore.ts
│
├── locales/ # 国际化语言包
│ ├── zh-CN.json # 中文
│ ├── en-US.json # 英文
│ └── index.ts # i18n 实例初始化(i18next / react-intl)
│
├── assets/ # 静态资源
│ ├── images/ # 图片(PNG、JPG、WebP)
│ ├── icons/ # SVG 图标
│ └── fonts/ # 自定义字体
│
├── config/ # 应用配置
│ ├── env.ts # 环境变量类型化封装
│ └── features.ts # Feature Flags 管理
│
├── types/ # 全局共享类型
│ ├── api.ts # API 响应/请求通用类型
│ ├── models.ts # 业务实体类型
│ └── global.d.ts # 全局类型扩展(图片模块声明等)
│
├── utils/ # 纯工具函数
│ ├── format.ts # 日期、数字、货币格式化
│ ├── validators.ts # 表单校验规则
│ └── storage.ts # LocalStorage / SessionStorage 封装
│
├── styles/ # 全局样式与主题
│ ├── global.css # 全局基础样式(reset / normalize)
│ ├── variables.css # CSS 变量(颜色、间距、字号)
│ ├── breakpoints.ts # 响应式断点常量
│ └── themes/ # 主题定义
│ ├── light.css # 亮色主题变量
│ ├── dark.css # 暗色主题变量
│ └── index.ts # 主题切换逻辑
│
└── constants/ # 全局常量
├── routes.ts # 路由路径常量
└── config.ts # 业务常量(分页大小、超时时间等)关键原则
组件设计规范
组件文件结构
ComponentName/
├── ComponentName.tsx
├── ComponentName.types.ts # 类型定义(如需独立)
├── ComponentName.module.css # 样式(如需)
├── hooks/
│ └── useComponentLogic.ts
├── components/
│ └── SubComponent.tsx
└── __tests__/
└── ComponentName.spec.tsx组件分层
页面组件 (Pages) → 路由映射、布局组合
└── 容器组件 (Containers) → 数据获取、状态编排
└── 业务组件 (Features) → 领域逻辑展示
└── 通用组件 (UI) → 纯展示,无业务耦合TypeScript 规范
interface TableProps<T extends Record<string, unknown>> {
data: T[];
columns: ColumnDef<T>[];
onRowClick?: (row: T) => void;
}Hooks 规范
自定义 Hook 设计
function useUserList(params: QueryParams) {
const [data, setData] = useState<User[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const fetch = useCallback(async () => {
setLoading(true);
setError(null);
try {
const res = await getUserList(params);
setData(res.list);
} catch (e) {
setError(e as Error);
} finally {
setLoading(false);
}
}, [params]);
useEffect(() => { fetch(); }, [fetch]);
return { data, loading, error, refetch: fetch };
}Hook 使用原则
路由规范
路由组织
// app/routes.tsx
const routes: RouteObject[] = [
{
path: '/',
element: <MainLayout />,
children: [
{ index: true, element: <DashboardPage /> },
{ path: 'users', element: <UserListPage /> },
{ path: 'users/:id', element: <UserDetailPage /> },
{ path: 'settings', element: <SettingsPage /> },
],
},
{ path: '/login', element: <LoginPage /> },
{ path: '*', element: <NotFoundPage /> },
];路由原则
const UserListPage = lazy(() => import('@/pages/UserList/UserListPage'));
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) return <Navigate to="/login" replace />;
return <>{children}</>;
}状态管理
| 状态类型 | 推荐方案 |
|----------|----------|
| 组件内临时 UI 状态 | `useState` / `useReducer` |
| 跨组件共享业务状态 | 全局 store(Zustand / Redux Toolkit) |
| 服务端数据缓存 | React Query / SWR |
| URL 驱动状态 | 路由参数 / `useSearchParams` |
| 表单状态 | React Hook Form / Formik |
核心原则
API 层规范
请求实例
// services/request.ts
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
});
request.interceptors.response.use(
(res) => res.data,
(error) => {
if (error.response?.status === 401) {
redirectToLogin();
}
return Promise.reject(normalizeError(error));
},
);API 函数
// features/user/api.ts
export function getUserList(params: UserQueryParams): Promise<PageResult<User>> {
return request.get('/users', { params });
}
export function updateUser(id: string, data: UpdateUserDTO): Promise<User> {
return request.put(`/users/${id}`, data);
}错误处理
Error Boundary
每个功能模块应有 Error Boundary 包裹,避免局部错误导致全页白屏:
<ErrorBoundary fallback={<ModuleErrorFallback />}>
<UserDashboard />
</ErrorBoundary>异步错误
Suspense 与懒加载
const HeavyChart = lazy(() => import('./components/HeavyChart'));
function Dashboard() {
return (
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart data={chartData} />
</Suspense>
);
}样式规范
测试规范
必须测试
测试风格
describe('UserForm', () => {
it('should submit with valid data', async () => {
const onSubmit = vi.fn();
render(<UserForm onSubmit={onSubmit} />);
await userEvent.type(screen.getByLabelText('用户名'), 'test');
await userEvent.click(screen.getByRole('button', { name: '提交' }));
expect(onSubmit).toHaveBeenCalledWith({ username: 'test' });
});
it('should show error on invalid input', async () => {
render(<UserForm onSubmit={vi.fn()} />);
await userEvent.click(screen.getByRole('button', { name: '提交' }));
expect(screen.getByText('用户名不能为空')).toBeInTheDocument();
});
});性能
反模式
输出检查清单
More tools from the same signal band
Order food/drinks (点餐) on an Android device paired as an OpenClaw node. Uses in-app menu and cart; add goods, view cart, submit order (demo, no real payment).
Sign plugins, rotate agent credentials without losing identity, and publicly attest to plugin behavior with verifiable claims and authenticated transfers.
The philosophical layer for AI agents. Maps behavior to Spinoza's 48 affects, calculates persistence scores, and generates geometric self-reports. Give your...