前言
如今,前端开发的行业领导者仍然是 React。它强大的抽象、活跃的社区和丰富的生态系统,让开发者能够创建从企业级程序到业余项目的各种应用。
然而,强大的功能也伴随着诸多的复杂性。如果你忽视了久经考验的最佳实践,React 应用很容易变得难以管理、不一致或难以维护。
为了在 2025 年创建可扩展、稳定、有效且干净的 React 应用程序,每个认真的开发人员都应该遵循这十条 React 最佳实践。
1. 构建小型、专注且可重用的组件
为什么重要:
React 是基于组件的。你的用户界面本质上是由可复用的小部件组成的树状结构。但只有当这些元素职责单一且高度集中时,它才能成功。
避免使用臃肿的“上帝组件”,将 UI、逻辑和副作用都放在一个文件中处理。
你应该做什么:
- 一个组件=一个目的
- 将大组件分解成较小的组件
- 在整个应用程序中重复使用组件以避免重复
例子
完成所有的事情,将其分解为:
<DashboardHeader />
<DashboardStats />
<DashboardActivity />
而不是
<Dashboard />
✅好处:
2. 使用带有 Hooks 的函数组件(优于类组件)
演变:
在 React 16.8 之前,类组件很常见。然而,目前,带有 hooks 的函数式组件被认为是最好的。
好处:
- 更清晰的语法
- 无需此绑定
- Hooks 使状态和生命周期管理模块化且可重用
需要掌握的常见钩子:
| Hook | Purpose |
|---------------------------|-----------------------------------------------------|
| `useState` | Local state |
| `useEffect` | Side effects (API calls, subscriptions) |
| `useRef` | Persisting mutable values without re-renders |
| `useContext` | Access global data without prop drilling |
| `useReducer` | More complex state logic (Redux-like) |
| `useCallback`, `useMemo` | Performance optimization |
| **Custom Hooks** | Abstract logic into reusable pieces |
3. 慎重提升状态(避免重复 Prop)
什么是提升状态?
当两个或多个组件需要访问相同状态时,应该解除最近的共同祖先。
需要避免的事情:
道具钻孔是通过几个不必要的中间组件插入道具的过程。
帮助工具:
- React Context 用于轻量级共享状态
- Zustand、Jotai 或 Redux Toolkit 适用于跨多个组件的复杂状态。
使用:
<UserContext.Provider value={user}>
<App />
</UserContext.Provider>
而不是:
<App user={user}>
<Sidebar user={user}>
<Profile user={user} />
</Sidebar>
</App>
4. 拥抱类型安全:TypeScript
为什么类型检查至关重要:
这种 JavaScript 是动态的。随着应用的增长,这种适应性可能会变成一个缺点。TypeScript(或 PropTypes)提供了一层保护。
TypeScript 的优点:
- 在运行之前捕获错误
- 强制一致的接口
- 通过自动完成和内联文档改善开发人员体验
TypeScript 示例:
type User = {
id: number;
name: string;
email: string;
};
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
}
仅将 PropTypes 用于简单的应用程序。对于任何实际应用,请使用 TypeScript。
5.创建可扩展的文件/文件夹结构
为什么结构很重要:
杂乱的结构变得难以控制。可扩展的设计实现了无缝入职,并促进了合作。
流行图案:
- 基于特征的结构
- 领域驱动结构
- 原子设计结构(原子、分子、生物体等)
建议设置:
src/
├── components/ # Reusable UI components
├── features/ # Feature-specific modules
│ └── auth/
│ └── dashboard/
├── hooks/ # Custom hooks
├── contexts/ # Context providers
├── pages/ # Route-level pages (Next.js)
├── utils/ # Helper functions
├── assets/ # Images, fonts, etc.
在主要文件夹中保留 README.md 来解释其结构。
6. 优化渲染:记忆重要内容
为什么要优化?
当组件的 props 或 state 发生变化时,React 会重新渲染它们。对于大型组件树来说,这可能会很昂贵。
记忆工具:
-
React.memo()
– 跳过纯函数组件的重新渲染
-
useMemo()
– 缓存昂贵的计算
-
useCallback()
– 记忆回调函数以防止子进程重新渲染
专业提示:
不要盲目优化。使用React Profiler来识别瓶颈。
7. 编写可重用的自定义钩子
为什么要使用自定义钩子?
钩子是可重复使用的逻辑容器。如果您需要在多个组件之间重复逻辑,请使用自定义钩子。
例子:
function useFetch(url) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url).then(res => res.json()).then(setData);
}, [url]);
return data;
}
用法如下:
const userData = useFetch('/api/user');
最佳实践:
前缀为use
保持钩子纯粹且可组合
放置在hooks
/ 文件夹内
8. 实现错误边界和回退 UI
React 并非万无一失
某个部分未解决的问题可能会导致用户界面崩溃。React 错误边界通过捕获错误提供温和的回退。
例子:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return <h1>Something went wrong.</h1>;
return this.props.children;
}
}
还可以使用:
-
Suspense
延迟加载
- 异步函数中的 Try/catch
- 外部库如
react-error-boundary
9. 测试组件:关注行为,而不是实现
为什么测试在 React 应用中很重要
测试的目标是建立对应用的信任,而不仅仅是发现缺陷。在现代 React 应用程序中进行测试可以确保您的用户界面 (UI) 在各种设备、状态和用户交互下都能正常运行。随着项目的进展,它可以保留您的逻辑,提供基于代码的文档,并避免回归。
思维转变: 将测试视为安全网,而不是琐事。它们让你能够大胆地重构,确保核心逻辑仍然有效。
您应该了解的测试类型
| Test Type | Purpose | Tools to Use |
|----------------|---------------------------------------------------|----------------------------------------------|
| Unit Tests | Test isolated components and logic | Jest, React Testing Library |
| Integration | Test how components work together | React Testing Library, Vitest |
| E2E Tests | Test full app behavior as a user would | Cypress, Playwright, TestCafe |
| Snapshot | Detect unintended UI changes (with caution) | Jest (with snapshot plugin) |
推荐的工具和生态系统
- React 测试库 (RTL):使用用户交互测试您的应用程序,重点关注事件、可访问性角色和 DOM。
- Jest:一个经过实战检验的 JavaScript 测试框架,具有内置的模拟、断言和快照。
- Cypress 或 Playwright:在真实的浏览器设置中进行彻底的端到端 (E2E) 测试。真正实现用户流程的自动化。
- MSW(模拟服务工作者):测试可以模拟 API 答案而不需要后端。
React 测试的最佳实践
1. 为用户行为编写测试,而不是内部实现
- 错误:检查内部状态或类名
- 好:点击按钮并检查结果是否显示
2. 使用getByRole, getByLabelText,
及getByText
结束getByTestId
3.测试边缘情况:
- 空状态
- API 响应缓慢
- 无效输入
- 移动视口尺寸(E2E)
4.保持测试快速且确定——通过模拟 API 并在需要时使用伪计时器来避免不稳定的测试
示例测试用例(React 测试库)
jsx
复制编辑
test("increments counter on click", () => {
render(<Counter />);
const button = screen.getByRole("button", { name: /count/i });
fireEvent.click(button);
expect(button).toHaveTextContent("1");
});
黄金法则:
- 测试用户关心的内容——行为、可访问性、交互。
- 避免测试组件内部——结构、状态变量或 DOM 树形状。
想要在所有平台上打造高性能应用?
10. 无障碍功能(a11y)是必须的,而不是可能的
为什么无障碍设施如此重要
无障碍设计是一项基本职责,而非次要考虑因素。每个人都应该能够使用您的应用,包括那些行动不便的人。包容性设计确保了用户能够平等地使用您的产品,无论他们是否有视力障碍、行动障碍或认知障碍。
在许多国家,无障碍设施也是一项法律要求(例如美国的 ADA、欧洲的 EN 301 549)。
a11y React 开发者最佳实践
1. 使用语义HTML
- 偏好本土元素
- 避免使用 div 进行交互式 UI — 屏幕阅读器会跳过它们
2. 确保键盘导航性
- 每个交互元素都应该可以通过 Tab 和 Enter 访问和操作
- 慎重使用 tabindex (避免 tabindex="0" 过载)
3. 需要时添加 ARIA 属性
- 使用 aria-label、aria-hidden、aria-live 为屏幕阅读器提供上下文
- 但是当语义 HTML 可以完成工作时,不要过度使用 ARIA
4. 为图片提供替代文本
- 对重要图片使用有意义的 alt=""
- 使用 alt="" 来隐藏装饰性的
5. 颜色对比度和焦点指示器
- 确保文本具有高对比度(根据 WCAG AA/AAA 检查)
- 不要删除焦点轮廓——如果需要,可以自定义它们
6. 表单错误处理
- 使用 aria-scribeby 链接表单错误
- 在模糊或提交时进行验证,而不仅仅是在更改时
确保年度合规性的工具
- axe DevTools(Chrome 扩展程序)——实时分析 WCAG 违规行为
- eslint-plugin-jsx-a11y — 查找缺失的角色、替代文本、标签陷阱
- Lighthouse (Chrome/CI) — 审核中的 a11y 评分
- 屏幕阅读器:NVDA(Windows)、VoiceOver(macOS)、ChromeVox
现实世界的可访问性审计技巧
- 仅使用键盘即可导航整个应用程序
- 使用屏幕阅读器浏览常见流程
- 使用 contrast-ratio.com 等工具测试颜色对比度
- 避免可能引发运动障碍的动画(尊重
prefers-reduced-motion
)
最终要点:以人为本,确保代码面向未来
React 为你提供工具。这些实践赋予你纪律。
- 清洁、模块化的组件。
- 现代功能(钩子、TypeScript、上下文)。
- 自信的、基于行为的测试。
- 无障碍且包容的体验。
- 可扩展的架构和文件结构。
- 安全的错误处理和性能调整。
当您将这些方法纳入到您的流程中时,您将创建可扩展、持久且令人愉悦的高质量软件。