本文最后更新于:2025-11-25T14:35:06+08:00
react剖析
使用脚手架搭建 react 项目
vite
1
| npm create vite@latest my-react-app
|
create-react-app
1
| npx create-react-app my-react-app --template typescript
|
JSX
什么是 JSX
JSX(JavaScript XML)是一种 JavaScript 的语法扩展,允许我们在 JavaScript 代码中编写类似 HTML 的结构。它使得组件的结构更加直观和易于维护。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const element = <h1 className="greeting">Hello, React!</h1>;
const name = 'World'; const element = <h1>Hello, {name}!</h1>;
const component = ( <div className="container"> <h1>标题</h1> <p>这是一段文本</p> </div> );
|
JSX 编译原理
JSX 通过编译器(Babel 的 @babel/preset-react 或 plugin-transform-react-jsx)进行转换。
React 17 之前的转换方式:
1 2 3 4 5 6 7 8 9
| const element = <h1 className="greeting">Hello, React!</h1>;
const element = React.createElement( 'h1', { className: 'greeting' }, 'Hello, React!' );
|
React 17+ 的新 JSX 转换:
1 2 3 4 5 6 7 8 9
| const element = <h1 className="greeting">Hello, React!</h1>;
import { jsx as _jsx } from 'react/jsx-runtime'; const element = _jsx('h1', { className: 'greeting', children: 'Hello, React!' });
|
在线转换地址:Babel 在线转换工具
JSX 规则
必须返回单个根元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| return ( <h1>标题</h1> <p>段落</p> );
return ( <div> <h1>标题</h1> <p>段落</p> </div> );
return ( <> <h1>标题</h1> <p>段落</p> </> );
|
所有标签必须闭合
1 2 3
| <img src="image.jpg" /> <br /> <input type="text" />
|
使用驼峰命名法
1 2 3
| <div className="container" onClick={handleClick}> <label htmlFor="input">标签</label> </div>
|
Hooks
Hooks 是 React 16.8 引入的特性,让函数组件能够使用状态和其他 React 特性。
useState
用于在函数组件中添加状态管理。
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { useState } from 'react';
function Counter() { const [count, setCount] = useState(0);
return ( <div> <p>当前计数: {count}</p> <button onClick={() => setCount(count + 1)}>增加</button> <button onClick={() => setCount(prev => prev - 1)}>减少</button> </div> ); }
|
重点:
- 状态更新是异步的
- 使用函数式更新可以获取最新状态:
setCount(prev => prev + 1)
- 初始值只在首次渲染时使用
useReducer
适合管理复杂的状态逻辑,类似于 Redux 的 reducer。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; case 'reset': return initialState; default: throw new Error('Unknown action type'); } }
function Counter() { const [state, dispatch] = useReducer(reducer, initialState);
return ( <div> <p>计数: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> <button onClick={() => dispatch({ type: 'reset' })}>重置</button> </div> ); }
|
useRef
创建一个可变的引用对象,用于访问 DOM 元素或存储不触发重渲染的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { useRef, useEffect } from 'react';
function TextInput() { const inputRef = useRef(null); const renderCount = useRef(0);
useEffect(() => { renderCount.current += 1; inputRef.current.focus(); });
return ( <div> <input ref={inputRef} type="text" /> <p>组件渲染次数: {renderCount.current}</p> </div> ); }
|
useRef 的两个主要用途:
- 访问 DOM 元素
- 存储不触发重渲染的可变值
useMemo
用于缓存计算结果,避免在每次渲染时进行昂贵的计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import { useMemo, useState } from 'react';
function ExpensiveComponent({ items }) { const [filter, setFilter] = useState('');
const filteredItems = useMemo(() => { console.log('过滤计算执行'); return items.filter(item => item.toLowerCase().includes(filter.toLowerCase()) ); }, [items, filter]);
return ( <div> <input value={filter} onChange={(e) => setFilter(e.target.value)} placeholder="搜索..." /> <ul> {filteredItems.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> </div> ); }
|
useCallback
返回一个记忆化的回调函数,避免子组件不必要的重渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import { useCallback, useState, memo } from 'react';
const Button = memo(({ onClick, children }) => { console.log('Button 渲染'); return <button onClick={onClick}>{children}</button>; });
function Parent() { const [count, setCount] = useState(0); const [other, setOther] = useState(0);
const increment = useCallback(() => { setCount(c => c + 1); }, []);
return ( <div> <p>Count: {count}</p> <p>Other: {other}</p> <Button onClick={increment}>增加 Count</Button> <button onClick={() => setOther(o => o + 1)}>增加 Other</button> </div> ); }
|
useMemo vs useCallback:
useMemo(() => fn) 缓存函数的返回值
useCallback(fn) 缓存函数本身
useContext
用于在组件树中共享数据,避免 props 层层传递。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function ThemeProvider({ children }) { const [theme, setTheme] = useState('light');
const toggleTheme = () => { setTheme(prev => prev === 'light' ? 'dark' : 'light'); };
return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); }
function ThemedButton() { const { theme, toggleTheme } = useContext(ThemeContext);
return ( <button onClick={toggleTheme} style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#333' : '#fff' }} > 当前主题: {theme} </button> ); }
function App() { return ( <ThemeProvider> <ThemedButton /> </ThemeProvider> ); }
|
useEffect
处理副作用(数据获取、订阅、DOM 操作等)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import { useEffect, useState } from 'react';
function DataFetcher({ userId }) { const [data, setData] = useState(null);
useEffect(() => { let cancelled = false;
async function fetchData() { const response = await fetch(`/api/user/${userId}`); const json = await response.json();
if (!cancelled) { setData(json); } }
fetchData();
return () => { cancelled = true; }; }, [userId]);
return <div>{data ? data.name : '加载中...'}</div>; }
|
useLayoutEffect
与 useEffect 类似,但在所有 DOM 变更后同步触发,用于需要同步测量布局的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { useLayoutEffect, useRef, useState } from 'react';
function MeasureComponent() { const ref = useRef(null); const [height, setHeight] = useState(0);
useLayoutEffect(() => { setHeight(ref.current.offsetHeight); }, []);
return ( <div ref={ref}> <p>组件高度: {height}px</p> </div> ); }
|
ref
ref 不仅可以作为 DOM 的引用,还可以作为不需要引起视图更新的数据存储。
ref 的使用场景
- 访问 DOM 元素
- 存储不触发重渲染的值(如定时器 ID、前一个 props 值等)
- 与第三方库集成(如视频播放器、图表库等)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { useRef, useEffect } from 'react';
function App() { const ref = useRef(null);
useEffect(() => { console.log(ref.current); ref.current.focus(); }, []);
return ( <div ref={ref}> Ref demo </div> ); }
|
ref 的常见用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function VideoPlayer({ src }) { const videoRef = useRef(null);
const play = () => { videoRef.current.play(); };
const pause = () => { videoRef.current.pause(); };
return ( <div> <video ref={videoRef} src={src} /> <button onClick={play}>播放</button> <button onClick={pause}>暂停</button> </div> ); }
|
forwardRef
forwardRef 允许组件将 ref 转发给子组件,使父组件能够访问子组件的 DOM 节点。
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { forwardRef, useRef } from 'react';
const CustomInput = forwardRef((props, ref) => { return <input ref={ref} {...props} />; });
function Form() { const inputRef = useRef(null);
const handleFocus = () => { inputRef.current.focus(); };
return ( <div> <CustomInput ref={inputRef} placeholder="输入文本" /> <button onClick={handleFocus}>聚焦输入框</button> </div> ); }
|
结合 useImperativeHandle
使用 useImperativeHandle 可以自定义暴露给父组件的实例值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import { forwardRef, useRef, useImperativeHandle } from 'react';
const FancyInput = forwardRef((props, ref) => { const inputRef = useRef(null);
useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); }, clear: () => { inputRef.current.value = ''; }, getValue: () => { return inputRef.current.value; } }));
return <input ref={inputRef} {...props} />; });
function App() { const fancyInputRef = useRef(null);
const handleClick = () => { fancyInputRef.current.focus(); console.log(fancyInputRef.current.getValue()); fancyInputRef.current.clear(); };
return ( <div> <FancyInput ref={fancyInputRef} placeholder="自定义输入框" /> <button onClick={handleClick}>操作输入框</button> </div> ); }
|
Suspense
Suspense 允许你在组件树中指定加载状态,通常用于代码分割和数据获取。
与 lazy 配合使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() { return ( <div> <h1>我的应用</h1> <Suspense fallback={<div>加载中...</div>}> <LazyComponent /> </Suspense> </div> ); }
|
嵌套 Suspense
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { Suspense, lazy } from 'react';
const Profile = lazy(() => import('./Profile')); const Posts = lazy(() => import('./Posts'));
function App() { return ( <Suspense fallback={<div>加载页面...</div>}> <div> <Suspense fallback={<div>加载个人资料...</div>}> <Profile /> </Suspense> <Suspense fallback={<div>加载文章...</div>}> <Posts /> </Suspense> </div> </Suspense> ); }
|
React 18 中的数据获取
React 18 支持在 Suspense 中进行数据获取(需要配合支持 Suspense 的数据获取库)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { Suspense } from 'react';
function UserProfile({ userId }) { const user = useUser(userId);
return ( <div> <h2>{user.name}</h2> <p>{user.email}</p> </div> ); }
function App() { return ( <Suspense fallback={<div>加载用户信息...</div>}> <UserProfile userId={1} /> </Suspense> ); }
|
lazy
React.lazy 用于动态导入组件,实现代码分割,减少初始加载时间。
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard')); const Settings = lazy(() => import('./Settings'));
function App() { const [page, setPage] = useState('dashboard');
return ( <div> <nav> <button onClick={() => setPage('dashboard')}>仪表板</button> <button onClick={() => setPage('settings')}>设置</button> </nav>
<Suspense fallback={<div>加载中...</div>}> {page === 'dashboard' && <Dashboard />} {page === 'settings' && <Settings />} </Suspense> </div> ); }
|
路由级别的代码分割
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { lazy, Suspense } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home')); const About = lazy(() => import('./pages/About')); const Contact = lazy(() => import('./pages/Contact'));
function App() { return ( <BrowserRouter> <Suspense fallback={<div>页面加载中...</div>}> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/contact" element={<Contact />} /> </Routes> </Suspense> </BrowserRouter> ); }
|
错误处理
结合 Error Boundary 处理加载失败的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import { Component, lazy, Suspense } from 'react';
class ErrorBoundary extends Component { state = { hasError: false };
static getDerivedStateFromError(error) { return { hasError: true }; }
componentDidCatch(error, info) { console.error('组件加载失败:', error, info); }
render() { if (this.state.hasError) { return <div>组件加载失败,请刷新页面重试</div>; } return this.props.children; } }
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() { return ( <ErrorBoundary> <Suspense fallback={<div>加载中...</div>}> <LazyComponent /> </Suspense> </ErrorBoundary> ); }
|
memo
React.memo 是一个高阶组件,用于优化函数组件的性能,避免不必要的重渲染。
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import { memo, useState } from 'react';
const ExpensiveComponent = memo(({ value }) => { console.log('ExpensiveComponent 渲染'); return <div>值: {value}</div>; });
function App() { const [count, setCount] = useState(0); const [text, setText] = useState('');
return ( <div> <ExpensiveComponent value={count} /> <button onClick={() => setCount(count + 1)}>增加计数</button>
<input value={text} onChange={(e) => setText(e.target.value)} placeholder="输入不会触发 ExpensiveComponent 重渲染" /> </div> ); }
|
自定义比较函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import { memo } from 'react';
const User = memo( ({ user }) => { console.log('User 组件渲染'); return ( <div> <h3>{user.name}</h3> <p>{user.email}</p> </div> ); }, (prevProps, nextProps) => { return prevProps.user.id === nextProps.user.id; } );
function App() { const [user, setUser] = useState({ id: 1, name: '张三', email: 'zhang@example.com' }); const [count, setCount] = useState(0);
return ( <div> <User user={user} /> <button onClick={() => setCount(count + 1)}> 计数: {count} (不会触发 User 重渲染) </button> <button onClick={() => setUser({ ...user, name: '李四' })}> 改变用户 (会触发 User 重渲染) </button> </div> ); }
|
memo 的使用场景
- 组件渲染开销大:组件包含复杂的计算或大量的 DOM 节点
- props 不经常变化:组件的 props 在多次渲染中保持不变
- 纯展示组件:组件只依赖 props,没有内部状态
注意事项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import { memo, useState } from 'react';
function App() { const [count, setCount] = useState(0);
return ( <MemoChild data={{ value: count }} // 每次都是新对象 onClick={() => {}} // 每次都是新函数 /> ); }
function App() { const [count, setCount] = useState(0);
const data = useMemo(() => ({ value: count }), [count]); const handleClick = useCallback(() => {}, []);
return ( <MemoChild data={data} onClick={handleClick} /> ); }
|
React 18 新特性
React 18 引入了多项重大更新,提升了性能和用户体验。
并发渲染(Concurrent Rendering)
React 18 的核心特性,允许 React 同时准备多个版本的 UI,并在需要时中断渲染。
关键概念:
- 可中断的渲染:React 可以暂停渲染工作,处理更高优先级的更新
- 自动批处理:多个状态更新会自动批处理成一次重渲染
- 过渡更新:区分紧急更新和非紧急更新
自动批处理(Automatic Batching)
React 18 会自动批处理所有状态更新,无论它们在哪里触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { useState } from 'react';
function App() { const [count, setCount] = useState(0); const [flag, setFlag] = useState(false);
function handleClick() { setCount(c => c + 1); setFlag(f => !f); }
setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); }, 1000);
console.log('渲染');
return ( <div> <button onClick={handleClick}> Count: {count}, Flag: {flag.toString()} </button> </div> ); }
|
退出批处理:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { flushSync } from 'react-dom';
function handleClick() { flushSync(() => { setCount(c => c + 1); });
flushSync(() => { setFlag(f => !f); }); }
|
Transitions API
用于标记非紧急的状态更新,让 React 知道某些更新可以被中断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import { useState, useTransition } from 'react';
function SearchResults() { const [isPending, startTransition] = useTransition(); const [query, setQuery] = useState(''); const [results, setResults] = useState([]);
function handleChange(e) { const value = e.target.value;
setQuery(value);
startTransition(() => { const filtered = largeList.filter(item => item.includes(value) ); setResults(filtered); }); }
return ( <div> <input value={query} onChange={handleChange} /> {isPending ? ( <div>搜索中...</div> ) : ( <ul> {results.map(item => ( <li key={item}>{item}</li> ))} </ul> )} </div> ); }
|
useDeferredValue
延迟更新某个值,让紧急更新优先处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import { useState, useDeferredValue, memo } from 'react';
function App() { const [text, setText] = useState(''); const deferredText = useDeferredValue(text);
return ( <div> <input value={text} onChange={(e) => setText(e.target.value)} /> {/* 输入框立即响应 */} <p>当前输入: {text}</p>
{/* 列表渲染使用延迟的值 */} <SlowList text={deferredText} /> </div> ); }
const SlowList = memo(({ text }) => { const items = []; for (let i = 0; i < 250; i++) { items.push(<li key={i}>{text} - {i}</li>); } return <ul>{items}</ul>; });
|
useId
生成唯一的 ID,适用于服务端渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import { useId } from 'react';
function NameFields() { const id = useId();
return ( <div> <label htmlFor={`${id}-firstName`}>名字:</label> <input id={`${id}-firstName`} type="text" />
<label htmlFor={`${id}-lastName`}>姓氏:</label> <input id={`${id}-lastName`} type="text" /> </div> ); }
function App() { return ( <> <NameFields /> <NameFields /> </> ); }
|
useSyncExternalStore
用于订阅外部数据源,确保在并发渲染中的一致性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import { useSyncExternalStore } from 'react';
const store = { state: { count: 0 }, listeners: new Set(),
subscribe(listener) { this.listeners.add(listener); return () => this.listeners.delete(listener); },
getSnapshot() { return this.state; },
increment() { this.state = { count: this.state.count + 1 }; this.listeners.forEach(listener => listener()); } };
function Counter() { const state = useSyncExternalStore( store.subscribe.bind(store), store.getSnapshot.bind(store) );
return ( <div> <p>Count: {state.count}</p> <button onClick={() => store.increment()}>增加</button> </div> ); }
|
useInsertionEffect
专门用于 CSS-in-JS 库,在 DOM 变更前同步触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import { useInsertionEffect } from 'react';
function useCSS(rule) { useInsertionEffect(() => { const style = document.createElement('style'); style.textContent = rule; document.head.appendChild(style);
return () => { document.head.removeChild(style); }; }, [rule]); }
function App() { useCSS(` .dynamic-class { color: red; font-size: 20px; } `);
return <div className="dynamic-class">动态样式</div>; }
|
新的服务端渲染架构
React 18 改进了 SSR 性能:
- 流式 SSR:服务器可以分块发送 HTML
- 选择性 Hydration:优先水化用户正在交互的部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { renderToPipeableStream } from 'react-dom/server';
function handleRequest(req, res) { const { pipe } = renderToPipeableStream(<App />, { onShellReady() { res.setHeader('Content-Type', 'text/html'); pipe(res); } }); }
import { hydrateRoot } from 'react-dom/client'; hydrateRoot(document.getElementById('root'), <App />);
|
Strict Mode 的变化
React 18 的严格模式会在开发环境中双重调用某些函数,帮助发现副作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { StrictMode } from 'react';
function App() { return ( <StrictMode> <MyComponent /> </StrictMode> ); }
|
性能优化最佳实践
1. 合理使用 memo、useMemo、useCallback
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
const SimpleButton = memo(({ label }) => <button>{label}</button>);
const ComplexList = memo(({ items }) => { return ( <ul> {items.map(item => ( <ExpensiveItem key={item.id} data={item} /> ))} </ul> ); });
function Parent() { const [count, setCount] = useState(0);
const handleClick = useCallback(() => { console.log('Clicked'); }, []);
return <ComplexList items={items} onClick={handleClick} />; }
|
2. 代码分割和懒加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard')); const Profile = lazy(() => import('./Profile'));
const HeavyChart = lazy(() => import('./HeavyChart'));
function App() { const [showChart, setShowChart] = useState(false);
return ( <div> <button onClick={() => setShowChart(true)}>显示图表</button> {showChart && ( <Suspense fallback={<div>加载中...</div>}> <HeavyChart /> </Suspense> )} </div> ); }
|
3. 虚拟化长列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { FixedSizeList } from 'react-window';
function VirtualList({ items }) { const Row = ({ index, style }) => ( <div style={style}> {items[index].name} </div> );
return ( <FixedSizeList height={600} itemCount={items.length} itemSize={50} width="100%" > {Row} </FixedSizeList> ); }
|
4. 避免不必要的渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function TodoList({ todos }) { return ( <ul> {todos.map(todo => ( // 使用稳定的 ID 作为 key <li key={todo.id}>{todo.text}</li> ))} </ul> ); }
const STATIC_OPTIONS = [ { value: '1', label: '选项 1' }, { value: '2', label: '选项 2' } ];
function Select() { return ( <select> {STATIC_OPTIONS.map(opt => ( <option key={opt.value} value={opt.value}> {opt.label} </option> ))} </select> ); }
|
5. 使用 Transitions 优化用户体验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import { useState, useTransition } from 'react';
function App() { const [isPending, startTransition] = useTransition(); const [tab, setTab] = useState('home');
function selectTab(nextTab) { startTransition(() => { setTab(nextTab); }); }
return ( <div> <TabButton isActive={tab === 'home'} onClick={() => selectTab('home')} > 首页 </TabButton> <TabButton isActive={tab === 'posts'} onClick={() => selectTab('posts')} > 文章 {isPending && '(加载中...)'} </TabButton>
{tab === 'home' && <HomePage />} {tab === 'posts' && <PostsPage />} </div> ); }
|
6. 图片优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function LazyImage({ src, alt }) { return ( <img src={src} alt={alt} loading="lazy" decoding="async" /> ); }
function ResponsiveImage({ src, alt }) { return ( <picture> <source media="(max-width: 600px)" srcSet={`${src}-small.jpg`} /> <source media="(max-width: 1200px)" srcSet={`${src}-medium.jpg`} /> <img src={`${src}-large.jpg`} alt={alt} /> </picture> ); }
|
7. 状态管理优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const StateContext = createContext(); const DispatchContext = createContext();
function Provider({ children }) { const [state, dispatch] = useReducer(reducer, initialState);
return ( <StateContext.Provider value={state}> <DispatchContext.Provider value={dispatch}> {children} </DispatchContext.Provider> </StateContext.Provider> ); }
function Component() { const dispatch = useContext(DispatchContext); const count = useContext(StateContext).count;
return <div onClick={() => dispatch({ type: 'INCREMENT' })}>{count}</div>; }
|
8. 使用 Web Workers 处理复杂计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { useEffect, useState } from 'react';
function useWorker(workerFn) { const [result, setResult] = useState(null);
useEffect(() => { const worker = new Worker( new URL('./worker.js', import.meta.url) );
worker.onmessage = (e) => setResult(e.data);
return () => worker.terminate(); }, []);
return result; }
self.onmessage = (e) => { const result = heavyComputation(e.data); self.postMessage(result); };
|
总结
React 18 带来了并发渲染、自动批处理、Transitions 等强大特性,极大提升了应用性能和用户体验。在实际开发中,应该:
- 合理使用 Hooks:理解每个 Hook 的使用场景和最佳实践
- 性能优化:避免过早优化,先测量再优化
- 代码分割:使用 lazy 和 Suspense 减少初始加载时间
- 并发特性:利用 Transitions 和 useDeferredValue 优化用户体验
- 类型安全:使用 TypeScript 提高代码质量
- 测试:编写单元测试和集成测试确保代码可靠性