构建可扩展的 React 应用:真实项目的经验教训

发布: (2026年1月17日 GMT+8 20:47)
5 min read
原文: Dev.to

Source: Dev.to

Borifan Dabasa

作者 Borifan Dabasa | 全栈开发者

“Just Start Coding”的问题

当我构建第一个 React 应用时,我把所有东西都塞进组件里,完全没有考虑结构。三个月后,我陷入了属性穿透、重复逻辑以及功能过于臃肿的组件之中。

听起来熟悉吗?

1. 可扩展的文件夹结构

以下是我现在所有 React 项目使用的结构:

src/
├── components/
│   ├── common/          # Reusable UI components
│   ├── layout/          # Layout components
│   └── features/        # Feature‑specific components
├── hooks/               # Custom hooks
├── context/             # Context providers
├── services/            # API calls
├── utils/               # Helper functions
├── constants/           # Constants and configs
└── pages/               # Page components

为什么这样有效: 每个文件夹只有单一职责。当我需要查找某个东西时,我知道该去哪里。

2. 组件组合胜于复杂性

我是吃了苦头才领悟到的。下面是我早期写的一个组件:

// ❌ Bad: God component doing everything
function UserDashboard() {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  // 200 lines of logic...

  return (
    {/* 300 lines of JSX */}
  );
}

现在我把它拆分了:

// ✅ Good: Composed components
function UserDashboard() {
  return (
    {/* composed sub‑components go here */}
  );
}

经验法则: 如果你的组件超过 150 行,通常说明它做的事太多了。

3. 自定义 Hook 用于逻辑复用

自定义 Hook 改变了我的生活。与其在组件之间复制逻辑,我把它抽取出来:

// hooks/useAuth.js
export function useAuth() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(setUser);
    setLoading(false);
    return unsubscribe;
  }, []);

  return { user, loading };
}

// Now use it anywhere
function Profile() {
  const { user, loading } = useAuth();

  if (loading) return ;
  return {user.name};
}

我使用自定义 Hook 来:

  • API 调用(useFetchuseApi
  • 表单处理(useForm
  • 本地存储(useLocalStorage
  • 防抖(useDebounce

4. 状态管理:保持简洁

我看到开发者立刻就去使用 Redux。我的做法是:

  • 本地状态 用于组件特定的数据
  • Context 用于全局数据(主题、认证)
  • Redux/Zustand 仅在 Context 变得混乱时才使用

在我的电商项目中,我使用 Context 来管理购物车和认证。就这么多。不需要 Redux。

// context/CartContext.js
export function CartProvider({ children }) {
  const [cart, setCart] = useState([]);

  const addToCart = (item) => {
    setCart([...cart, item]);
  };

  return (
    {/* provide cart context to children */}
    {children}
  );
}

5. 性能优化

代码拆分

// Lazy load heavy components
const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    {/* render routes with Dashboard */}
  );
}

记忆化

// Prevent unnecessary re‑renders
const ExpensiveComponent = memo(({ data }) => {
  return {/* Heavy computation */};
});

虚拟列表

对于我的加密货币追踪器,包含 1,000 多种币种,我使用 react-window

import { FixedSizeList } from 'react-window';

{/* render CoinRow inside FixedSizeList */}

6. 错误边界

这在生产环境的错误中救了我:

class ErrorBoundary extends Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return ;
    }
    return this.props.children;
  }
}

// 包裹你的应用
<ErrorBoundary>
  <App />
</ErrorBoundary>

7. 环境变量

切勿硬编码 API URL:

# .env
REACT_APP_API_URL=https://api.example.com
REACT_APP_API_KEY=your_key_here
// Use in code
const API_URL = process.env.REACT_APP_API_URL;

实际案例:我的博客项目

在我的 Next.js 博客(live at),我实现了:

  • 基于文件的路由 用于自动代码拆分
  • ISR(增量静态再生) 实现快速加载
  • TypeScript 提供类型安全
# Tailwind CSS for consistent styling  

**Result?** Lighthouse score of **95+** and sub‑second load times.

关键要点

  • 结构很重要 – 按功能组织,而不是按文件类型
  • 组合,而非复杂化 – 小而专注的组件
  • 提取逻辑 – 自定义 Hook 是你的好帮手
  • 智能优化 – 优化前先进行测量
  • 为错误做好规划 – 错误边界能拯救生产环境

接下来是什么?

  • Next.js 14 中的 Server Components
  • 用于更好数据获取的 React Query
  • 用于大型应用的 Micro‑frontends

想要看到这些模式的实际效果吗?请查看我在 GitHub 上的项目。

有问题吗?

留下评论或通过 联系。

与我联系

标签: #WebDevelopment #JavaScript #Frontend #MERN

Back to Blog

相关文章

阅读更多 »

待办应用

引言 在完成我的第一个以逻辑为中心的项目 Counters 后,我想在复杂度上迈出下一步——不是通过改进 UI,而是通过...