Skip to main content

frontend-patterns

React、Next.js、状态管理、性能优化和UI最佳实践的前端开发模式。

Aller à l'installation

Informations de source

Dépôt
mit-network/everything-claude-code
Dernière activité de la source
30 mars 2026 à 01:21
Langue détectée de SKILL.md
chinois
Étoiles
80
Forks
16

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
frontend-patterns
description
React、Next.js、状态管理、性能优化和UI最佳实践的前端开发模式。
origin
ECC
# 前端开发模式 适用于 React、Next.js 和高性能用户界面的现代前端模式。 ## 何时激活 * 构建 React 组件(组合、属性、渲染) * 管理状态(useState、useReducer、Zustand、Context) * 实现数据获取(SWR、React Query、服务器组件) * 优化性能(记忆化、虚拟化、代码分割) * 处理表单(验证、受控输入、Zod 模式) * 处理客户端路由和导航 * 构建可访问、响应式的 UI 模式 ## 组件模式 ### 组合优于继承 ```typescript // PASS: GOOD: Component composition interface CardProps { children: React.ReactNode variant?: 'default' | 'outlined' } export function Card({ children, variant = 'default' }: CardProps) { return <div className={`card card-${variant}`}>{children}</div> } export function CardHeader({ children }: { children: React.ReactNode }) { return <div className="card-header">{children}</div> } export function CardBody({ children }: { children: React.ReactNode }) { return <div className="card-body">{children}</div> } // Usage <Card> <CardHeader>Title</CardHeader> <CardBody>Content</CardBody> </Card> ``` ### 复合组件 ```typescript interface TabsContextValue { activeTab: string setActiveTab: (tab: string) => void } const TabsContext = createContext<TabsContextValue | undefined>(undefined) export function Tabs({ children, defaultTab }: { children: React.ReactNode defaultTab: string }) { const [activeTab, setActiveTab] = useState(defaultTab) return ( <TabsContext.Provider value={{ activeTab, setActiveTab }}> {children} </TabsContext.Provider> ) } export function TabList({ children }: { children: React.ReactNode }) { return <div className="tab-list">{children}</div> } export function Tab({ id, children }: { id: string, children: React.ReactNode }) { const context = useContext(TabsContext) if (!context) throw new Error('Tab must be used within Tabs') return ( <button className={context.activeTab === id ? 'active' : ''} onClick={() => context.setActiveTab(id)} > {children} </button> ) } // Usage <Tabs defaultTab="overview"> <TabList> <Tab id="overview">Overview</Tab> <Tab id="details">Details</Tab> </TabList> </Tabs> ``` ### 渲染属性模式 ```typescript interface DataLoaderProps<T> { url: string children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode } export function DataLoader<T>({ url, children }: DataLoaderProps<T>) { const [data, setData] = useState<T | null>(null) const [loading, setLoading] = useState(true) const [error, setError] = useState<Error | null>(null) useEffect(() => { fetch(url) .then(res => res.json()) .then(setData) .catch(setError) .finally(() => setLoading(false)) }, [url]) return <>{children(data, loading, error)}</> } // Usage <DataLoader<Market[]> url="/api/markets"> {(markets, loading, error) => { if (loading) return <Spinner /> if (error) return <Error error={error} /> return <MarketList markets={markets!} /> }} </DataLoader> ``` ## 自定义 Hooks 模式 ### 状态管理 Hook ```typescript export function useToggle(initialValue = false): [boolean, () => void] { const [value, setValue] = useState(initialValue) const toggle = useCallback(() => { setValue(v => !v) }, []) return [value, toggle] } // Usage const [isOpen, toggleOpen] = useToggle() ``` ### 异步数据获取 Hook ```typescript interface UseQueryOptions<T> { onSuccess?: (data: T) => void onError?: (error: Error) => void enabled?: boolean } export function useQuery<T>( key: string, fetcher: () => Promise<T>, options?: UseQueryOptions<T> ) { const [data, setData] = useState<T | null>(null) const [error, setError] = useState<Error | null>(null) const [loading, setLoading] = useState(false) const refetch = useCallback(async () => { setLoading(true) setError(null) try { const result = await fetcher() setData(result) options?.onSuccess?.(result) } catch (err) { const error = err as Error setError(error) options?.onError?.(error) } finally { setLoading(false) } }, [fetcher, options]) useEffect(() => { if (options?.enabled !== false) { refetch() } }, [key, refetch, options?.enabled]) return { data, error, loading, refetch } } // Usage const { data: markets, loading, error, refetch } = useQuery( 'markets', () => fetch('/api/markets').then(r => r.json()), { onSuccess: data => console.log('Fetched', data.length, 'markets'), onError: err => console.error('Failed:', err) } ) ``` ### 防抖 Hook ```typescript export function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState<T>(value) useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value) }, delay) return () => clearTimeout(handler) }, [value, delay]) return debouncedValue } // Usage const [searchQuery, setSearchQuery] = useState('') const debouncedQuery = useDebounce(searchQuery, 500) useEffect(() => { if (debouncedQuery) { performSearch(debouncedQuery) } }, [debouncedQuery]) ``` ## 状态管理模式 ### Context + Reducer 模式 ```typescript interface State { markets: Market[] selectedMarket: Market | null loading: boolean } type Action = | { type: 'SET_MARKETS'; payload: Market[] } | { type: 'SELECT_MARKET'; payload: Market } | { type: 'SET_LOADING'; payload: boolean } function reducer(state: State, action: Action): State { switch (action.type) { case 'SET_MARKETS': return { ...state, markets: action.payload } case 'SELECT_MARKET': return { ...state, selectedMarket: action.payload } case 'SET_LOADING': return { ...state, loading: action.payload } default: return state } } const MarketContext = createContext<{ state: State dispatch: Dispatch<Action> } | undefined>(undefined) export function MarketProvider({ children }: { children: React.ReactNode }) { const [state, dispatch] = useReducer(reducer, { markets: [], selectedMarket: null, loading: false }) return ( <MarketContext.Provider value={{ state, dispatch }}> {children} </MarketContext.Provider> ) } export function useMarkets() { const context = useContext(MarketContext) if (!context) throw new Error('useMarkets must be used within MarketProvider') return context } ``` ## 性能优化 ### 记忆化 ```typescript // PASS: useMemo for expensive computations const sortedMarkets = useMemo(() => { return markets.sort((a, b) => b.volume - a.volume) }, [markets]) // PASS: useCallback for functions passed to children const handleSearch = useCallback((query: string) => { setSearchQuery(query) }, []) // PASS: React.memo for pure components export const MarketCard = React.memo<MarketCardProps>(({ market }) => { return ( <div className="market-card"> <h3>{market.name}</h3> <p>{market.description}</p> </div> ) }) ``` ### 代码分割与懒加载 ```typescript import { lazy, Suspense } from 'react' // PASS: Lazy load heavy components const HeavyChart = lazy(() => import('./HeavyChart')) const ThreeJsBackground = lazy(() => import('./ThreeJsBackground')) export function Dashboard() { return ( <div> <Suspense fallback={<ChartSkeleton />}> <HeavyChart data={data} /> </Suspense> <Suspense fallback={null}> <ThreeJsBackground /> </Suspense> </div> ) } ``` ### 长列表虚拟化 ```typescript import { useVirtualizer } from '@tanstack/react-virtual' export function VirtualMarketList({ markets }: { markets: Market[] }) { const parentRef = useRef<HTMLDivElement>(null) const virtualizer = useVirtualizer({ count: markets.length, getScrollElement: () => parentRef.current, estimateSize: () => 100, // Estimated row height overscan: 5 // Extra items to render }) return ( <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}> <div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }} > {virtualizer.getVirtualItems().map(virtualRow => ( <div key={virtualRow.index} style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: `${virtualRow.size}px`, transform: `translateY(${virtualRow.start}px)` }} > <MarketCard market={markets[virtualRow.index]} /> </div> ))} </div> </div> ) } ``` ## 表单处理模式 ### 带验证的受控表单 ```typescript interface FormData { name: string description: string endDate: string } interface FormErrors { name?: string description?: string endDate?: string } export function CreateMarketForm() { const [formData, setFormData] = useState<FormData>({ name: '', description: '', endDate: '' }) const [errors, setErrors] = useState<FormErrors>({}) const validate = (): boolean => { const newErrors: FormErrors = {} if (!formData.name.trim()) { newErrors.name = 'Name is required' } else if (formData.name.length > 200) { newErrors.name = 'Name must be under 200 characters' } if (!formData.description.trim()) { newErrors.description = 'Description is required' } if (!formData.endDate) { newErrors.endDate = 'End date is required' } setErrors(newErrors) return Object.keys(newErrors).length === 0 } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!validate()) return try { await createMarket(formData) // Success handling } catch (error) { // Error handling } } return ( <form onSubmit={handleSubmit}> <input value={formData.name} onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))} placeholder="Market name" /> {errors.name && <span className="error">{errors.name}</span>} {/* Other fields */} <button type="submit">Create Market</button> </form>
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub