React学习路径与实践指南
文章目录
- 第一阶段:React 基础深入
- 1.1 环境搭建和项目初始化
- 1.2 JSX 深度解析
- 1.3 组件深度解析
- 1.4 Props 深入理解
- 1.5 事件处理深度解析
- 第二阶段:Hooks 深度解析
- 2.1 useState 高级用法
- 2.2 useEffect 深度解析
- 2.3 自定义 Hooks 深度实践
- 第三阶段:性能优化深度解析
- 3.1 React.memo 和 useMemo 优化
- 3.2 代码分割和懒加载
- 第四阶段:高级模式和架构
- 4.1 复合组件模式
- 4.2 状态管理架构
- 第五阶段:测试和部署
- 5.1 完整测试策略
- 5.2 部署和性能监控
- 第六阶段:React 18 新特性
- 6.1 并发特性
- 学习建议进阶
第一阶段:React 基础深入
1.1 环境搭建和项目初始化
# 使用 Vite (推荐)
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev# 或使用 Create React App
npx create-react-app my-react-app
cd my-react-app
npm start
1.2 JSX 深度解析
// JSX 基础语法
function JSXExamples() {const name = "React Developer";const isLoggedIn = true;const numbers = [1, 2, 3, 4, 5];const user = {firstName: "John",lastName: "Doe"};// JSX 表达式return (<div className="container">{/* 注释写法 */}<h1>Hello, {name}!</h1>{/* 条件渲染 */}{isLoggedIn ? (<p>Welcome back!</p>) : (<p>Please log in.</p>)}{/* 逻辑与操作符 */}{isLoggedIn && <button>Logout</button>}{/* 列表渲染 */}<ul>{numbers.map(number => (<li key={number}>{number * 2}</li>))}</ul>{/* 对象展开 */}<UserCard {...user} />{/* 样式应用 */}<div style={{padding: '20px',backgroundColor: '#f0f0f0',borderRadius: '8px'}}>Styled div</div></div>);
}// JSX 编译原理
// 下面的 JSX:
const element = <h1 className="greeting">Hello, world!</h1>;// 会被编译为:
const element = React.createElement('h1',{ className: 'greeting' },'Hello, world!'
);
1.3 组件深度解析
// 函数组件 vs 类组件对比
import React, { Component } from 'react';// 函数组件 (现代推荐)
function FunctionalComponent({ title, children }) {return (<div><h2>{title}</h2>{children}</div>);
}// 类组件 (传统方式,了解即可)
class ClassComponent extends Component {constructor(props) {super(props);this.state = {count: 0};// 绑定方法this.handleClick = this.handleClick.bind(this);}handleClick() {this.setState(prevState => ({count: prevState.count + 1}));}componentDidMount() {console.log('Component mounted');}render() {return (<div><p>Count: {this.state.count}</p><button onClick={this.handleClick}>Increment</button></div>);}
}// 组件组合模式
function Card({ title, content, actions }) {return (<div className="card"><div className="card-header"><h3>{title}</h3></div><div className="card-body">{content}</div>{actions && (<div className="card-actions">{actions}</div>)}</div>);
}function App() {return (<div><Cardtitle="User Profile"content={<p>This is user profile content</p>}actions={<button onClick={() => alert('Action!')}>Click Me</button>}/></div>);
}
1.4 Props 深入理解
// Props 验证和默认值
import PropTypes from 'prop-types';function UserProfile({ user, showAvatar = true, size = 'medium',onEdit,children
}) {return (<div className={`user-profile ${size}`}>{showAvatar && user.avatar && (<img src={user.avatar} alt={user.name}className="avatar"/>)}<div className="user-info"><h3>{user.name}</h3><p>{user.email}</p>{user.bio && <p className="bio">{user.bio}</p>}</div>{onEdit && (<button onClick={() => onEdit(user)}>Edit Profile</button>)}{children}</div>);
}// PropTypes 验证
UserProfile.propTypes = {user: PropTypes.shape({id: PropTypes.number.isRequired,name: PropTypes.string.isRequired,email: PropTypes.string.isRequired,avatar: PropTypes.string,bio: PropTypes.string}).isRequired,showAvatar: PropTypes.bool,size: PropTypes.oneOf(['small', 'medium', 'large']),onEdit: PropTypes.func,children: PropTypes.node
};// 默认 Props
UserProfile.defaultProps = {showAvatar: true,size: 'medium'
};// 使用示例
function App() {const user = {id: 1,name: 'John Doe',email: 'john@example.com',avatar: 'https://example.com/avatar.jpg',bio: 'Frontend Developer'};const handleEdit = (user) => {console.log('Editing user:', user);};return (<UserProfile user={user}onEdit={handleEdit}size="large"><div className="additional-content">Additional user information</div></UserProfile>);
}
1.5 事件处理深度解析
function EventHandlingExamples() {const [formData, setFormData] = useState({username: '',email: '',topic: 'react'});const [clicks, setClicks] = useState(0);// 合成事件系统const handleClick = (event) => {// React 的 SyntheticEventconsole.log('Event type:', event.type);console.log('Target:', event.target);console.log('Current target:', event.currentTarget);// 事件池 - React 17 之前需要特别注意// event.persist() // React 16 中如果需要异步访问事件需要调用setClicks(prev => prev + 1);};// 表单处理const handleInputChange = (event) => {const { name, value, type, checked } = event.target;setFormData(prev => ({...prev,[name]: type === 'checkbox' ? checked : value}));};// 阻止默认行为const handleSubmit = (event) => {event.preventDefault();console.log('Form submitted:', formData);};// 事件委托const handleListClick = (event) => {if (event.target.tagName === 'LI') {console.log('Clicked item:', event.target.textContent);}};// 自定义参数传递const handleAction = (action, data) => {console.log(`Action: ${action}`, data);};return (<div>{/* 基本事件 */}<button onClick={handleClick}>Clicked {clicks} times</button>{/* 表单事件 */}<form onSubmit={handleSubmit}><inputname="username"value={formData.username}onChange={handleInputChange}placeholder="Username"/><inputname="email"type="email"value={formData.email}onChange={handleInputChange}placeholder="Email"/><select name="topic" value={formData.topic}onChange={handleInputChange}><option value="react">React</option><option value="vue">Vue</option><option value="angular">Angular</option></select><button type="submit">Submit</button></form>{/* 事件委托 */}<ul onClick={handleListClick}><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>{/* 自定义参数 */}<button onClick={() => handleAction('delete', { id: 1 })}>Delete Item</button></div>);
}
第二阶段:Hooks 深度解析
2.1 useState 高级用法
import { useState, useRef } from 'react';function AdvancedState() {// 1. 函数式更新const [count, setCount] = useState(0);// 2. 对象状态const [user, setUser] = useState({name: '',age: 0,preferences: {theme: 'light',notifications: true}});// 3. 数组状态const [items, setItems] = useState([]);// 4. 使用 useRef 保持稳定引用const renderCount = useRef(0);renderCount.current++;// 批量更新示例const handleMultipleUpdates = () => {// React 18 会自动批处理setCount(prev => prev + 1);setCount(prev => prev + 1);setCount(prev => prev + 1);};// 深层状态更新const updateUserPreference = (key, value) => {setUser(prev => ({...prev,preferences: {...prev.preferences,[key]: value}}));};// 使用函数更新避免依赖const addItem = (newItem) => {setItems(prev => [...prev, {id: Date.now(),...newItem}]);};// 状态重置const resetState = () => {setCount(0);setUser({name: '',age: 0,preferences: {theme: 'light',notifications: true}});setItems([]);};return (<div><p>Render count: {renderCount.current}</p><p>Count: {count}</p><div><inputvalue={user.name}onChange={(e) => setUser(prev => ({ ...prev, name: e.target.value }))}placeholder="Name"/><button onClick={() => updateUserPreference('theme', 'dark')}>Set Dark Theme</button></div><button onClick={handleMultipleUpdates}>Increment Multiple Times</button><button onClick={() => addItem({ name: 'New Item' })}>Add Item</button><button onClick={resetState}>Reset All</button></div>);
}
2.2 useEffect 深度解析
import { useState, useEffect, useRef } from 'react';function EffectExamples() {const [data, setData] = useState(null);const [userId, setUserId] = useState(1);const [windowSize, setWindowSize] = useState({width: window.innerWidth,height: window.innerHeight});const intervalRef = useRef();const mountedRef = useRef(true);// 1. 数据获取 EffectuseEffect(() => {let cancelled = false;const fetchData = async () => {try {const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);const userData = await response.json();if (!cancelled) {setData(userData);}} catch (error) {if (!cancelled) {console.error('Fetch error:', error);}}};fetchData();// 清理函数 - 防止已卸载组件设置状态return () => {cancelled = true;};}, [userId]); // 依赖数组// 2. 事件监听 EffectuseEffect(() => {const handleResize = () => {setWindowSize({width: window.innerWidth,height: window.innerHeight});};window.addEventListener('resize', handleResize);// 清理事件监听器return () => {window.removeEventListener('resize', handleResize);};}, []); // 空依赖数组 - 只在挂载和卸载时运行// 3. 定时器 EffectuseEffect(() => {intervalRef.current = setInterval(() => {console.log('Timer tick');}, 1000);// 清理定时器return () => {clearInterval(intervalRef.current);};}, []);// 4. 组合多个 EffectuseEffect(() => {// 组件挂载时执行mountedRef.current = true;console.log('Component mounted');return () => {// 组件卸载时执行mountedRef.current = false;console.log('Component unmounted');};}, []);// 5. 条件执行 EffectuseEffect(() => {if (data) {console.log('Data updated:', data);// 基于数据的其他操作}}, [data]);return (<div><h2>useEffect Examples</h2><button onClick={() => setUserId(prev => prev + 1)}>Next User ({userId})</button>{data && (<div><h3>{data.name}</h3><p>{data.email}</p></div>)}<p>Window size: {windowSize.width} x {windowSize.height}</p></div>);
}
2.3 自定义 Hooks 深度实践
// 高级自定义 Hook - 数据获取 with 重试、缓存
function useApi(url, options = {}) {const {method = 'GET',body = null,headers = {},cacheTime = 5 * 60 * 1000, // 5分钟缓存retries = 3} = options;const [state, setState] = useState({data: null,loading: true,error: null,retryCount: 0});const cache = useRef(new Map());const abortControllerRef = useRef();useEffect(() => {const fetchData = async () => {// 检查缓存const cacheKey = `${method}:${url}:${JSON.stringify(body)}`;const cached = cache.current.get(cacheKey);if (cached && Date.now() - cached.timestamp < cacheTime) {setState(prev => ({...prev,data: cached.data,loading: false,error: null}));return;}// 取消之前的请求if (abortControllerRef.current) {abortControllerRef.current.abort();}abortControllerRef.current = new AbortController();setState(prev => ({ ...prev, loading: true, error: null }));try {const response = await fetch(url, {method,body: body ? JSON.stringify(body) : null,headers: {'Content-Type': 'application/json',...headers},signal: abortControllerRef.current.signal});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const result = await response.json();// 缓存结果cache.current.set(cacheKey, {data: result,timestamp: Date.now()});setState(prev => ({...prev,data: result,loading: false,error: null,retryCount: 0}));} catch (error) {if (error.name === 'AbortError') {return; // 请求被取消,不处理错误}if (state.retryCount < retries) {// 重试逻辑setTimeout(() => {setState(prev => ({...prev,retryCount: prev.retryCount + 1}));}, 1000 * Math.pow(2, state.retryCount)); // 指数退避} else {setState(prev => ({...prev,loading: false,error: error.message}));}}};fetchData();return () => {if (abortControllerRef.current) {abortControllerRef.current.abort();}};}, [url, method, JSON.stringify(body), state.retryCount]);const refetch = () => {setState(prev => ({ ...prev, retryCount: 0 }));};return {...state,refetch};
}// 使用示例
function UserProfile({ userId }) {const { data: user, loading, error, refetch } = useApi(`https://jsonplaceholder.typicode.com/users/${userId}`,{ retries: 3 });if (loading) return <div>Loading user...</div>;if (error) return <div>Error: {error}</div>;return (<div><h2>{user.name}</h2><p>{user.email}</p><button onClick={refetch}>Reload</button></div>);
}// 更多实用自定义 Hooks
function useLocalStorage(key, initialValue) {const [storedValue, setStoredValue] = useState(() => {try {const item = window.localStorage.getItem(key);return item ? JSON.parse(item) : initialValue;} catch (error) {console.error(`Error reading localStorage key "${key}":`, error);return initialValue;}});const setValue = (value) => {try {const valueToStore = value instanceof Function ? value(storedValue) : value;setStoredValue(valueToStore);window.localStorage.setItem(key, JSON.stringify(valueToStore));} catch (error) {console.error(`Error setting localStorage key "${key}":`, error);}};return [storedValue, setValue];
}function useDebounce(value, delay) {const [debouncedValue, setDebouncedValue] = useState(value);useEffect(() => {const handler = setTimeout(() => {setDebouncedValue(value);}, delay);return () => {clearTimeout(handler);};}, [value, delay]);return debouncedValue;
}function useToggle(initialValue = false) {const [value, setValue] = useState(initialValue);const toggle = useCallback(() => {setValue(prev => !prev);}, []);const setTrue = useCallback(() => {setValue(true);}, []);const setFalse = useCallback(() => {setValue(false);}, []);return [value, toggle, setTrue, setFalse];
}
第三阶段:性能优化深度解析
3.1 React.memo 和 useMemo 优化
import { memo, useMemo, useCallback, useState } from 'react';// 1. React.memo 用于组件记忆
const ExpensiveComponent = memo(function ExpensiveComponent({ data, onUpdate, config
}) {console.log('ExpensiveComponent rendered');// 昂贵的计算const processedData = useMemo(() => {console.log('Processing data...');return data.map(item => ({...item,processed: heavyComputation(item, config)}));}, [data, config]); // 依赖数组return (<div>{processedData.map(item => (<ExpensiveItem key={item.id} item={item} onUpdate={onUpdate}/>))}</div>);
});// 2. 嵌套组件优化
const ExpensiveItem = memo(function ExpensiveItem({ item, onUpdate }) {console.log('ExpensiveItem rendered:', item.id);return (<div className="item"><span>{item.name}</span><button onClick={() => onUpdate(item.id)}>Update</button></div>);
});// 3. 使用 useCallback 优化函数引用
function OptimizedParent() {const [data, setData] = useState(generateInitialData());const [filter, setFilter] = useState('');const [config, setConfig] = useState({ algorithm: 'fast' });// useCallback 缓存函数const handleUpdate = useCallback((id) => {setData(prev => prev.map(item => item.id === id ? { ...item, updated: true } : item));}, []);// useMemo 缓存计算结果const filteredData = useMemo(() => {return data.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()));}, [data, filter]);// 复杂配置对象记忆const memoizedConfig = useMemo(() => ({...config,timestamp: Date.now()}), [config]);return (<div><input value={filter}onChange={(e) => setFilter(e.target.value)}placeholder="Filter..."/><button onClick={() => setConfig({ algorithm: 'precise' })}>Change Algorithm</button><ExpensiveComponent data={filteredData} onUpdate={handleUpdate}config={memoizedConfig}/></div>);
}// 模拟昂贵计算
function heavyComputation(item, config) {// 模拟耗时操作let result = 0;for (let i = 0; i < 1000000; i++) {result += Math.sqrt(i) * Math.sin(i);}return result;
}function generateInitialData() {return Array.from({ length: 10 }, (_, i) => ({id: i + 1,name: `Item ${i + 1}`,value: Math.random() * 100}));
}
3.2 代码分割和懒加载
import { lazy, Suspense, useState } from 'react';// 1. 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const UserSettings = lazy(() => import('./UserSettings'));
const AnalyticsDashboard = lazy(() => import('./AnalyticsDashboard').then(module => ({default: module.AnalyticsDashboard}))
);// 2. 预加载策略
const preloadComponents = {settings: () => import('./UserSettings'),analytics: () => import('./AnalyticsDashboard')
};function LazyLoadingApp() {const [currentView, setCurrentView] = useState('home');const [preloaded, setPreloaded] = useState(new Set());// 预加载组件const preloadComponent = (componentName) => {if (!preloaded.has(componentName) {preloadComponents[componentName]?.();setPreloaded(prev => new Set(prev).add(componentName));}};const renderContent = () => {switch (currentView) {case 'settings':return <UserSettings />;case 'analytics':return <AnalyticsDashboard />;default:return <Home />;}};return (<div><nav><button onClick={() => setCurrentView('home')}>Home</button><button onClick={() => setCurrentView('settings')}onMouseEnter={() => preloadComponent('settings')}>Settings</button><button onClick={() => setCurrentView('analytics')}onMouseEnter={() => preloadComponent('analytics')}>Analytics</button></nav><Suspense fallback={<LoadingSpinner />}>{renderContent()}</Suspense></div>);
}// 自定义加载组件
function LoadingSpinner() {return (<div className="loading-spinner"><div className="spinner"></div><p>Loading component...</p></div>);
}// 错误边界处理懒加载错误
class LazyLoadErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false, error: null };}static getDerivedStateFromError(error) {return { hasError: true, error };}componentDidCatch(error, errorInfo) {console.error('Lazy load error:', error, errorInfo);}render() {if (this.state.hasError) {return (<div className="error-fallback"><h3>Failed to load component</h3><button onClick={() => this.setState({ hasError: false })}>Retry</button></div>);}return this.props.children;}
}
第四阶段:高级模式和架构
4.1 复合组件模式
// 1. 复合组件模式 - 标签式 API
import { createContext, useContext } from 'react';const TabsContext = createContext();function Tabs({ children, defaultActiveTab }) {const [activeTab, setActiveTab] = useState(defaultActiveTab);const contextValue = useMemo(() => ({activeTab,setActiveTab}), [activeTab]);return (<TabsContext.Provider value={contextValue}><div className="tabs">{children}</div></TabsContext.Provider>);
}function TabList({ children }) {return (<div className="tab-list">{children}</div>);
}function Tab({ tabId, children }) {const { activeTab, setActiveTab } = useContext(TabsContext);const isActive = activeTab === tabId;return (<buttonclassName={`tab ${isActive ? 'active' : ''}`}onClick={() => setActiveTab(tabId)}>{children}</button>);
}function TabPanels({ children }) {return (<div className="tab-panels">{children}</div>);
}function TabPanel({ tabId, children }) {const { activeTab } = useContext(TabsContext);if (activeTab !== tabId) return null;return (<div className="tab-panel">{children}</div>);
}// 使用示例
function App() {return (<Tabs defaultActiveTab="profile"><TabList><Tab tabId="profile">Profile</Tab><Tab tabId="settings">Settings</Tab><Tab tabId="billing">Billing</Tab></TabList><TabPanels><TabPanel tabId="profile"><h2>Profile Information</h2><p>User profile details...</p></TabPanel><TabPanel tabId="settings"><h2>Settings</h2><p>Application settings...</p></TabPanel><TabPanel tabId="billing"><h2>Billing Information</h2><p>Billing details...</p></TabPanel></TabPanels></Tabs>);
}// 2. 渲染属性模式 (Render Props)
class DataFetcher extends Component {state = {data: null,loading: true,error: null};async componentDidMount() {try {const response = await fetch(this.props.url);const data = await response.json();this.setState({ data, loading: false });} catch (error) {this.setState({ error, loading: false });}}render() {return this.props.children(this.state);}
}// 使用渲染属性
function UserList() {return (<DataFetcher url="/api/users">{({ data, loading, error }) => {if (loading) return <div>Loading...</div>;if (error) return <div>Error: {error.message}</div>;return (<ul>{data.map(user => (<li key={user.id}>{user.name}</li>))}</ul>);}}</DataFetcher>);
}
4.2 状态管理架构
// 高级 Context + useReducer 状态管理
import { createContext, useReducer, useContext, useMemo } from 'react';// 1. 创建 Context
const AppStateContext = createContext();
const AppDispatchContext = createContext();// 2. 初始状态和 Reducer
const initialState = {user: null,theme: 'light',notifications: [],sidebar: {open: false,selected: 'home'}
};function appReducer(state, action) {switch (action.type) {case 'SET_USER':return {...state,user: action.payload};case 'SET_THEME':return {...state,theme: action.payload};case 'ADD_NOTIFICATION':return {...state,notifications: [...state.notifications,{id: Date.now(),...action.payload}]};case 'REMOVE_NOTIFICATION':return {...state,notifications: state.notifications.filter(n => n.id !== action.payload)};case 'TOGGLE_SIDEBAR':return {...state,sidebar: {...state.sidebar,open: !state.sidebar.open}};case 'SET_SIDEBAR_SELECTED':return {...state,sidebar: {...state.sidebar,selected: action.payload}};default:return state;}
}// 3. Action Creators
const actionCreators = {setUser: (user) => ({ type: 'SET_USER', payload: user }),setTheme: (theme) => ({ type: 'SET_THEME', payload: theme }),addNotification: (notification) => ({ type: 'ADD_NOTIFICATION', payload: notification }),removeNotification: (id) => ({ type: 'REMOVE_NOTIFICATION', payload: id }),toggleSidebar: () => ({ type: 'TOGGLE_SIDEBAR' }),setSidebarSelected: (selected) => ({ type: 'SET_SIDEBAR_SELECTED', payload: selected })
};// 4. Provider 组件
function AppStateProvider({ children }) {const [state, dispatch] = useReducer(appReducer, initialState);// 记忆化 context valueconst contextValue = useMemo(() => state, [state]);return (<AppStateContext.Provider value={contextValue}><AppDispatchContext.Provider value={dispatch}>{children}</AppDispatchContext.Provider></AppStateContext.Provider>);
}// 5. 自定义 Hooks 访问状态和操作
function useAppState() {const context = useContext(AppStateContext);if (!context) {throw new Error('useAppState must be used within AppStateProvider');}return context;
}function useAppDispatch() {const context = useContext(AppDispatchContext);if (!context) {throw new Error('useAppDispatch must be used within AppStateProvider');}return context;
}// 组合 Hook - 提供便捷的操作方法
function useAppActions() {const dispatch = useAppDispatch();return useMemo(() => ({setUser: (user) => dispatch(actionCreators.setUser(user)),setTheme: (theme) => dispatch(actionCreators.setTheme(theme)),addNotification: (notification) => dispatch(actionCreators.addNotification(notification)),removeNotification: (id) => dispatch(actionCreators.removeNotification(id)),toggleSidebar: () => dispatch(actionCreators.toggleSidebar()),setSidebarSelected: (selected) => dispatch(actionCreators.setSidebarSelected(selected))}), [dispatch]);
}// 6. 使用示例
function UserProfile() {const { user, theme } = useAppState();const { setTheme, addNotification } = useAppActions();const handleThemeChange = () => {const newTheme = theme === 'light' ? 'dark' : 'light';setTheme(newTheme);addNotification({type: 'success',message: `Theme changed to ${newTheme}`});};return (<div className={`user-profile ${theme}`}><h2>{user?.name}</h2><button onClick={handleThemeChange}>Switch to {theme === 'light' ? 'dark' : 'light'} theme</button></div>);
}// 7. 应用根组件
function App() {return (<AppStateProvider><UserProfile />{/* 其他组件 */}</AppStateProvider>);
}
第五阶段:测试和部署
5.1 完整测试策略
// Component.test.js - 组件测试
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AppStateProvider } from './AppStateContext';// 测试工具函数
const renderWithProviders = (ui, { initialState } = {}) => {return render(<AppStateProvider initialState={initialState}>{ui}</AppStateProvider>);
};// 模拟 API
jest.mock('./api', () => ({fetchUser: jest.fn(() => Promise.resolve({id: 1,name: 'Test User',email: 'test@example.com'}))
}));describe('UserProfile', () => {test('renders user information', () => {const initialState = {user: { id: 1, name: 'John Doe', email: 'john@example.com' }};renderWithProviders(<UserProfile />, { initialState });expect(screen.getByText('John Doe')).toBeInTheDocument();expect(screen.getByText('john@example.com')).toBeInTheDocument();});test('handles theme change', async () => {const user = userEvent.setup();renderWithProviders(<UserProfile />);const themeButton = screen.getByRole('button', { name: /switch to dark theme/i });await user.click(themeButton);expect(screen.getByText(/theme changed to dark/i)).toBeInTheDocument();});test('loads user data on mount', async () => {renderWithProviders(<UserProfile />);await waitFor(() => {expect(screen.getByText('Test User')).toBeInTheDocument();});});test('handles errors gracefully', async () => {const mockConsoleError = jest.spyOn(console, 'error').mockImplementation();require('./api').fetchUser.mockRejectedValueOnce(new Error('API Error'));renderWithProviders(<UserProfile />);await waitFor(() => {expect(screen.getByText(/error loading user/i)).toBeInTheDocument();});mockConsoleError.mockRestore();});
});// Hook 测试
import { renderHook, act } from '@testing-library/react';
import { useAppActions, AppStateProvider } from './AppStateContext';test('useAppActions provides correct actions', () => {const wrapper = ({ children }) => (<AppStateProvider>{children}</AppStateProvider>);const { result } = renderHook(() => useAppActions(), { wrapper });act(() => {result.current.setTheme('dark');});// 验证状态更新expect(result.current).toHaveProperty('setTheme');expect(result.current).toHaveProperty('addNotification');
});// 集成测试
describe('App Integration', () => {test('full user journey', async () => {const user = userEvent.setup();renderWithProviders(<App />);// 导航到设置页面const settingsTab = screen.getByRole('tab', { name: /settings/i });await user.click(settingsTab);// 修改设置const themeSelect = screen.getByLabelText(/theme/i);await user.selectOptions(themeSelect, 'dark');// 验证更改expect(screen.getByText(/settings saved/i)).toBeInTheDocument();});
});
5.2 部署和性能监控
// 性能监控 Hook
import { useEffect, useRef } from 'react';function usePerformanceMonitor(componentName) {const mountTime = useRef(performance.now());const renderCount = useRef(0);useEffect(() => {const mountDuration = performance.now() - mountTime.current;console.log(`${componentName} mounted in ${mountDuration.toFixed(2)}ms`);// 发送到监控服务if (window.analytics) {window.analytics.track('component_mount', {component: componentName,duration: mountDuration});}}, []);useEffect(() => {renderCount.current++;if (renderCount.current > 1) {console.warn(`${componentName} re-rendered ${renderCount.current} times. ` +'Consider optimizing with React.memo or useMemo.');}});
}// 错误边界 with 错误上报
class ErrorBoundaryWithReporting extends Component {state = { hasError: false };static getDerivedStateFromError(error) {return { hasError: true };}componentDidCatch(error, errorInfo) {// 上报错误到监控服务console.error('Error caught by boundary:', error, errorInfo);if (window.errorReporting) {window.errorReporting.captureException(error, {extra: errorInfo,componentStack: errorInfo.componentStack});}}render() {if (this.state.hasError) {return (<div className="error-fallback"><h2>Something went wrong</h2><button onClick={() => this.setState({ hasError: false })}>Try Again</button></div>);}return this.props.children;}
}// 生产环境配置
// vite.config.js 或 webpack.config.js
export default {build: {// 代码分割配置rollupOptions: {output: {manualChunks: {vendor: ['react', 'react-dom'],utils: ['lodash', 'date-fns']}}},// 资源优化assetsInlineLimit: 4096,// 源代码映射sourcemap: true},plugins: [// 环境变量注入// 打包分析// 压缩优化]
};
第六阶段:React 18 新特性
6.1 并发特性
import { useState, useTransition, useDeferredValue, Suspense } from 'react';function ConcurrentFeatures() {const [query, setQuery] = useState('');const [isPending, startTransition] = useTransition();const deferredQuery = useDeferredValue(query);// 使用 useTransition 处理非紧急更新const handleSearch = (newQuery) => {startTransition(() => {setQuery(newQuery);});};return (<div><inputvalue={query}onChange={(e) => handleSearch(e.target.value)}placeholder="Search..."/>{/* 显示过渡状态 */}{isPending && <div className="loading-indicator">Searching...</div>}{/* 使用延迟值避免界面卡顿 */}<Suspense fallback={<div>Loading results...</div>}><SearchResults query={deferredQuery} /></Suspense></div>);
}// 自动批处理示例
function BatchUpdateExample() {const [count, setCount] = useState(0);const [flag, setFlag] = useState(false);// React 18 中这些更新会自动批处理const handleClick = () => {setCount(c => c + 1);setFlag(f => !f);// 在 React 18 中,这只会导致一次重渲染};return (<div><button onClick={handleClick}>Count: {count}, Flag: {flag.toString()}</button></div>);
}
这个详细扩展版涵盖了React开发的各个方面,从基础概念到高级模式,包含了实际项目中的最佳实践和常见问题的解决方案。建议按照阶段顺序学习,每个阶段都要通过实际项目来巩固知识。
学习建议进阶
- 代码阅读: 阅读优秀的开源React项目源码
- 性能分析: 学习使用React DevTools进行性能分析
- 类型安全: 结合TypeScript提升代码质量
- 工程化: 学习构建工具、CI/CD流程
- 架构设计: 研究大型应用的架构模式
精通React不仅仅是了解API,更重要的是理解其设计理念、性能特性和最佳实践。持续学习和实践是关键!