太棒了!👏 你已经掌握了 React 的性能优化,现在是时候整合所有知识,进入 高级架构设计阶段。
—— 打造专业级、可维护的 React 应用
🎯 目标:
- 封装通用逻辑为可复用的自定义 Hook
- 设计清晰的项目结构
- 实现“关注点分离”与“高内聚低耦合”
- 构建企业级项目骨架
一、为什么需要架构?🏗️
随着项目变大,你会遇到:
| 问题 | 表现 |
|---|---|
| ❌ 代码重复 | 多个页面都写 useEffect 请求数据 |
| ❌ 状态混乱 | 登录逻辑散落在各组件中 |
| ❌ 难以测试 | 组件职责太多,无法独立验证 |
| ❌ 团队协作难 | 没有统一规范,命名随意 |
✅ 解决方案:良好的架构 + 自定义 Hook
二、经典自定义 Hook 封装实战 💡
我们来封装几个在真实项目中高频使用的 Hook。
🪝 1. useApi:统一 API 请求管理(带 loading/error)
// hooks/useApi.ts
import { useState, useEffect } from 'react';
interface UseApiResult<T> {
data: T | null;
loading: boolean;
error: string | null;
refetch: () => void; // 支持手动刷新
}
function useApi<T>(url: string): UseApiResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
setData(json);
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [url]);
return { data, loading, error, refetch: fetchData };
}
export default useApi;
✅ 使用方式:
function UserList() {
const { data: users, loading, error } = useApi<User[]>('/api/users');
if (loading) return <Spinner />;
if (error) return <Alert message={`加载失败: ${error}`} />;
return (
<ul>
{users?.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
✅ 从此不再重复写 loading/error/data 三件套!
🪝 2. useForm:表单状态管理(支持校验)
// hooks/useForm.ts
import { useState } from 'react';
interface FormOptions<T> {
validate?: (values: T) => Partial<Record<keyof T, string>>;
onSubmit: (values: T) => void;
}
function useForm<T extends object>(
initialValues: T,
options: FormOptions<T>
) {
const { onSubmit, validate } = options;
const [values, setValues] = useState<T>(initialValues);
const [errors, setErrors] = useState<Partial<T>>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
// 实时清除错误
if (errors[name as keyof T]) {
setErrors(prev => ({ ...prev, [name]: undefined }));
}
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const validationErrors = validate?.(values) || {};
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
setIsSubmitting(true);
onSubmit(values);
}
};
return {
values,
errors,
isSubmitting,
handleChange,
handleSubmit,
setValues, // 可用于重置
};
}
✅ 使用方式:
function LoginForm() {
const { values, errors, handleChange, handleSubmit } = useForm(
{ username: '', password: '' },
{
validate: (values) => {
const errs = {} as any;
if (!values.username) errs.username = '请输入用户名';
if (!values.password) errs.password = '请输入密码';
return errs;
},
onSubmit: (data) => {
console.log('提交:', data);
},
}
);
return (
<form onSubmit={handleSubmit}>
<input
name="username"
value={values.username}
onChange={handleChange}
/>
{errors.username && <span>{errors.username}</span>}
<input
name="password"
type="password"
value={values.password}
onChange={handleChange}
/>
{errors.password && <span>{errors.password}</span>}
<button type="submit">登录</button>
</form>
);
}
🪝 3. useAuth:全局认证状态管理
// hooks/useAuth.ts
import { useState, createContext, useContext } from 'react';
interface AuthUser {
id: number;
name: string;
token: string;
}
interface AuthContextType {
user: AuthUser | null;
login: (username: string, password: string) => Promise<void>;
logout: () => void;
isLoading: boolean;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<AuthUser | null>(() => {
const saved = localStorage.getItem('user');
return saved ? JSON.parse(saved) : null;
});
const [isLoading, setIsLoading] = useState(false);
const login = async (username: string, password: string) => {
setIsLoading(true);
try {
const res = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
});
const userData = await res.json();
setUser(userData);
localStorage.setItem('user', JSON.stringify(userData));
} catch (err) {
alert('登录失败');
} finally {
setIsLoading(false);
}
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
};
return (
<AuthContext.Provider value={{ user, login, logout, isLoading }}>
{children}
</AuthContext.Provider>
);
}
// 自定义 Hook 使用 context
export const useAuth = () => {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth 必须在 AuthProvider 内使用');
return ctx;
};
✅ 在任意组件中使用:
function Profile() {
const { user, logout } = useAuth();
if (!user) return <LoginButton />;
return (
<div>
<p>欢迎 {user.name}</p>
<button onClick={logout}>退出登录</button>
</div>
);
}
三、推荐项目结构(最佳实践)📁
src/
├── components/ # 通用 UI 组件
│ ├── Button.tsx
│ ├── Modal.tsx
│ └── Spinner.tsx
│
├── pages/ # 页面级组件(路由对应)
│ ├── Home.tsx
│ ├── Login.tsx
│ └── Dashboard.tsx
│
├── hooks/ # 自定义 Hook
│ ├── useApi.ts
│ ├── useForm.ts
│ └── useAuth.ts
│
├── store/ # 状态管理(可选)
│ ├── authStore.ts
│ └── cartStore.ts
│
├── utils/ # 工具函数
│ ├── apiClient.ts # 封装 axios/fetch
│ └── validators.ts # 校验逻辑
│
├── contexts/ # Context 定义(可合并到 hooks)
│ └── ThemeContext.tsx
│
├── routes/ # 路由配置
│ └── index.tsx
│
├── assets/ # 静态资源
│ ├── images/
│ └── styles/
│
├── App.tsx
└── main.tsx
✅ 特点:
- 按功能划分(Feature-based),非按类型
- 易于扩展和团队协作
- 支持懒加载和代码分割
四、进阶技巧与原则 ✅
1. 单一职责原则
每个 Hook / 组件只做一件事
// ❌ 不要在一个 Hook 里又处理表单又发请求
// ✅ 分成 useForm + useApi
2. 可组合性
Hook 可以互相调用
function useUserData(userId: number) {
const { data, loading } = useApi(`/api/users/${userId}`);
const { updateCache } = useCache(); // 可组合其他 Hook
return { user: data, loading, updateCache };
}
3. 错误边界(Error Boundary)
防止一个组件崩溃导致整个应用白屏
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h2>出错了,请刷新</h2>;
}
return this.props.children;
}
}
// 使用
<ErrorBoundary>
<ProblematicComponent />
</ErrorBoundary>
⚠️ 注意:函数式组件不能直接使用 getDerivedStateFromError,需用类组件包裹。
五、总结:高级 React 开发者的核心能力
| 能力 | 表现 |
|---|---|
| 🔧 自定义 Hook | 能抽象通用逻辑,提升复用性 |
| 🗂️ 项目结构设计 | 清晰、可维护、易协作 |
| ⚡ 性能意识 | 知道何时该优化,如何分析瓶颈 |
| 🔐 状态管理 | 合理选择 Context / Zustand / Redux |
| 🧩 组件抽象 | 提炼出可复用的 UI + 逻辑单元 |
| 📦 工程化思维 | 支持懒加载、测试、国际化等 |
🎯 下一步建议
你现在完全可以胜任中大型 React 项目的开发。接下来可以深入:
- Next.js:服务端渲染、静态生成、API 路由
- 测试:Jest + React Testing Library
- CI/CD:自动化部署流程
- 微前端:模块联邦(Module Federation)
- 设计系统:Storybook + 组件库搭建
如果你想要,我可以为你生成一个 完整的企业级 React 项目模板(Vite + TS + React Router + Zustand + TailwindCSS) ,包含上述所有最佳实践。
是否需要?🙂