前端架构设计:别再写面条代码了
前端架构设计别再写面条代码了毒舌时刻这代码写得跟网红滤镜似的——仅供参考。各位前端同行咱们今天聊聊前端架构。别告诉我你的项目还是一堆面条代码那感觉就像在一团乱麻里找线头——能找到但费劲。为什么你需要好的架构最近看到一个项目3000 行的组件200 个 useState逻辑和 UI 混在一起新成员看了一周还不敢改代码。我就想问你是在写代码还是在写天书反面教材// 反面教材面条代码 // pages/UserDashboard.tsx export default function UserDashboard() { const [user, setUser] useState(null); const [orders, setOrders] useState([]); const [products, setProducts] useState([]); const [loading, setLoading] useState(false); const [error, setError] useState(null); const [selectedTab, setSelectedTab] useState(orders); const [searchQuery, setSearchQuery] useState(); const [sortBy, setSortBy] useState(date); const [filterStatus, setFilterStatus] useState(all); const [currentPage, setCurrentPage] useState(1); const [itemsPerPage, setItemsPerPage] useState(10); const [showModal, setShowModal] useState(false); const [modalData, setModalData] useState(null); const [cart, setCart] useState([]); const [wishlist, setWishlist] useState([]); const [notifications, setNotifications] useState([]); // ... 还有 50 个 state useEffect(() { // 300 行的数据获取逻辑 // 夹杂着条件判断、错误处理、数据转换 }, [/* 20 个依赖 */]); useEffect(() { // 200 行的 WebSocket 逻辑 }, []); useEffect(() { // 150 行的本地存储同步逻辑 }, [cart, wishlist]); // 100 个处理函数 const handleSearch () { /* ... */ }; const handleSort () { /* ... */ }; const handleFilter () { /* ... */ }; const handlePageChange () { /* ... */ }; const handleAddToCart () { /* ... */ }; // ... 还有 95 个 // 500 行的 JSX return ( div {/* 嵌套 10 层的 JSX */} /div ); } // 总计3000 行 // 维护难度毒舌点评这代码3000 行一个组件你是在写代码还是在写小说前端架构的正确姿势1. 分层架构src/ ├── app/ # 应用层Next.js App Router │ ├── (auth)/ # 路由组 │ ├── api/ # API 路由 │ ├── layout.tsx # 根布局 │ ├── page.tsx # 首页 │ └── globals.css ├── features/ # 功能模块按功能组织 │ ├── auth/ # 认证功能 │ │ ├── api/ # API 调用 │ │ ├── components/ # 组件 │ │ ├── hooks/ # Hooks │ │ ├── stores/ # 状态管理 │ │ ├── types/ # 类型定义 │ │ └── utils/ # 工具函数 │ ├── orders/ │ ├── products/ │ └── user/ ├── shared/ # 共享资源 │ ├── components/ # 通用组件 │ ├── hooks/ # 通用 Hooks │ ├── lib/ # 第三方库配置 │ ├── types/ # 全局类型 │ └── utils/ # 通用工具 └── widgets/ # 复杂组件可选2. 功能模块结构// features/orders/ // api/getOrders.ts import { apiClient } from /shared/lib/api; import { Order } from ../types; export async function getOrders(params: GetOrdersParams): PromiseOrder[] { const response await apiClient.get(/orders, { params }); return response.data; } // hooks/useOrders.ts import { useQuery } from tanstack/react-query; import { getOrders } from ../api/getOrders; export function useOrders(params: GetOrdersParams) { return useQuery({ queryKey: [orders, params], queryFn: () getOrders(params) }); } // components/OrderList.tsx use client; import { useOrders } from ../hooks/useOrders; import { OrderCard } from ./OrderCard; export function OrderList({ userId }: { userId: string }) { const { data: orders, isLoading } useOrders({ userId }); if (isLoading) return OrderListSkeleton /; return ( div classNamespace-y-4 {orders?.map(order ( OrderCard key{order.id} order{order} / ))} /div ); } // stores/orderStore.ts import { create } from zustand; import { Order } from ../types; interface OrderState { selectedOrder: Order | null; setSelectedOrder: (order: Order | null) void; } export const useOrderStore createOrderState((set) ({ selectedOrder: null, setSelectedOrder: (order) set({ selectedOrder: order }) })); // types/index.ts export interface Order { id: string; userId: string; items: OrderItem[]; total: number; status: OrderStatus; createdAt: Date; } export type OrderStatus pending | processing | shipped | delivered;3. 组件设计原则// 正确姿势单一职责组件 // 1. 展示组件Presentational // components/ui/Button.tsx interface ButtonProps { variant?: primary | secondary | danger; size?: sm | md | lg; isLoading?: boolean; children: React.ReactNode; onClick?: () void; } export function Button({ variant primary, size md, isLoading, children, onClick }: ButtonProps) { return ( button className{cn( rounded font-medium transition-colors, variants[variant], sizes[size], isLoading opacity-50 cursor-not-allowed )} onClick{onClick} disabled{isLoading} {isLoading ? Spinner / : children} /button ); } // 2. 容器组件Container // features/orders/components/OrderManager.tsx use client; import { useOrders } from ../hooks/useOrders; import { useOrderStore } from ../stores/orderStore; import { OrderList } from ./OrderList; import { OrderDetail } from ./OrderDetail; export function OrderManager({ userId }: { userId: string }) { const { data: orders, isLoading } useOrders({ userId }); const { selectedOrder, setSelectedOrder } useOrderStore(); if (isLoading) return OrderListSkeleton /; return ( div classNamegrid grid-cols-2 gap-4 OrderList orders{orders} onSelect{setSelectedOrder} selectedId{selectedOrder?.id} / OrderDetail order{selectedOrder} / /div ); } // 3. 组合模式 // features/orders/components/OrderCard.tsx import { Card, CardHeader, CardContent } from /shared/components/Card; import { Badge } from /shared/components/Badge; import { OrderStatusBadge } from ./OrderStatusBadge; import { OrderTotal } from ./OrderTotal; interface OrderCardProps { order: Order; onClick?: () void; isSelected?: boolean; } export function OrderCard({ order, onClick, isSelected }: OrderCardProps) { return ( Card isSelected{isSelected} onClick{onClick} CardHeader div classNameflex justify-between span订单 #{order.id}/span OrderStatusBadge status{order.status} / /div /CardHeader CardContent OrderTotal items{order.items} / /CardContent /Card ); }4. 状态管理策略// 正确姿势状态分层 // 1. 服务端状态React Query // features/user/hooks/useUser.ts import { useQuery, useMutation } from tanstack/react-query; export function useUser(userId: string) { return useQuery({ queryKey: [user, userId], queryFn: () fetchUser(userId) }); } export function useUpdateUser() { const queryClient useQueryClient(); return useMutation({ mutationFn: updateUser, onSuccess: (data) { queryClient.setQueryData([user, data.id], data); } }); } // 2. 客户端全局状态Zustand // stores/uiStore.ts import { create } from zustand; interface UIState { sidebarOpen: boolean; theme: light | dark; toggleSidebar: () void; setTheme: (theme: light | dark) void; } export const useUIStore createUIState((set) ({ sidebarOpen: false, theme: light, toggleSidebar: () set((state) ({ sidebarOpen: !state.sidebarOpen })), setTheme: (theme) set({ theme }) })); // 3. 本地组件状态useState // 只在组件内部使用的状态 function SearchInput() { const [query, setQuery] useState(); const [isFocused, setIsFocused] useState(false); // ... } // 4. URL 状态 // 使用 URL 作为状态存储 import { useSearchParams } from next/navigation; function ProductFilter() { const [searchParams, setSearchParams] useSearchParams(); const category searchParams.get(category); const sort searchParams.get(sort); const setFilter (key: string, value: string) { setSearchParams({ [key]: value }); }; }毒舌点评这才叫架构职责清晰易于维护新人能快速上手。实战技巧架构最佳实践1. 依赖规则// 依赖方向上层 - 下层 // app - features - shared // 不允许反向依赖 // ✅ 正确feature 使用 shared import { Button } from /shared/components/Button; // ❌ 错误shared 依赖 feature // shared/components/Button.tsx import { useOrderStore } from /features/orders/stores/orderStore; // 错误2. 类型定义// shared/types/index.ts // 全局共享类型 export interface User { id: string; email: string; name: string; } export interface APIResponseT { data: T; message?: string; } // features/orders/types/index.ts // 功能特定类型 import { User } from /shared/types; export interface Order { id: string; userId: string; user?: User; // 关联类型 items: OrderItem[]; total: number; }3. API 层抽象// shared/lib/api/client.ts import axios from axios; export const apiClient axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, timeout: 10000 }); apiClient.interceptors.request.use((config) { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); // features/auth/api/login.ts import { apiClient } from /shared/lib/api; export async function login(credentials: LoginCredentials) { const response await apiClient.post(/auth/login, credentials); return response.data; }4. 错误处理// shared/lib/error/index.ts export class AppError extends Error { constructor( message: string, public code: string, public statusCode?: number ) { super(message); this.name AppError; } } export function handleError(error: unknown): AppError { if (error instanceof AppError) { return error; } if (error instanceof Error) { return new AppError(error.message, UNKNOWN_ERROR); } return new AppError(Unknown error, UNKNOWN_ERROR); } // 使用 import { useErrorBoundary } from react-error-boundary; function OrderList() { const { showBoundary } useErrorBoundary(); const { data, error } useOrders(); if (error) { showBoundary(handleError(error)); return null; } // ... }5. 常见陷阱// 陷阱1循环依赖 // features/a/index.ts import { B } from /features/b; // ❌ // features/b/index.ts import { A } from /features/a; // ❌ // 解决提取公共部分到 shared // 陷阱2过度抽象 // ❌ 为了抽象而抽象 export function useDataT(key: string, fetcher: () PromiseT) { // 过度抽象的 hook } // ✅ 具体明确的 hook export function useOrders(params: GetOrdersParams) { return useQuery({ queryKey: [orders, params], queryFn: () getOrders(params) }); } // 陷阱3状态管理混乱 // ❌ 到处都用全局状态 const [localState, setLocalState] useState(); // 应该用 useState // ✅ 选择合适的状态管理 // 本地状态 - useState // 共享状态 - Context / Zustand // 服务端状态 - React Query // URL 状态 - useSearchParams最后想说的好的前端架构不是可选的是项目成功的关键。别再写面条代码了——用上好的架构你的代码会易于维护、易于扩展。记住好的架构应该是简单的每个部分都有明确的职责新人能快速理解。