Appearance
渲染性能问题排查 - React应用性能优化实战
1. 渲染性能基础
1.1 React渲染流程
typescript
const renderingProcess = {
阶段: [
'1. Render阶段: 计算需要更新的内容',
'2. Commit阶段: 实际更新DOM',
'3. Layout阶段: 浏览器布局',
'4. Paint阶段: 浏览器绘制'
],
React工作流: [
'触发更新 (setState, props变化)',
'调用render函数',
'Diff算法比较',
'生成Fiber树',
'提交更新到DOM',
'执行副作用'
],
性能瓶颈: [
'不必要的重渲染',
'复杂的render函数',
'大列表渲染',
'Context滥用',
'昂贵的计算',
'过多的DOM操作'
]
};1.2 性能指标
javascript
// 关键性能指标
const performanceMetrics = {
FCP: {
名称: 'First Contentful Paint',
定义: '首次内容绘制时间',
目标: '< 1.8s'
},
LCP: {
名称: 'Largest Contentful Paint',
定义: '最大内容绘制时间',
目标: '< 2.5s'
},
FID: {
名称: 'First Input Delay',
定义: '首次输入延迟',
目标: '< 100ms'
},
CLS: {
名称: 'Cumulative Layout Shift',
定义: '累积布局偏移',
目标: '< 0.1'
},
TTI: {
名称: 'Time to Interactive',
定义: '可交互时间',
目标: '< 3.8s'
},
TBT: {
名称: 'Total Blocking Time',
定义: '总阻塞时间',
目标: '< 300ms'
}
};
// 测量自定义指标
function measurePerformance() {
// React组件渲染时间
const start = performance.now();
// 渲染组件...
const end = performance.now();
console.log(`Render time: ${end - start}ms`);
}2. 识别性能问题
2.1 React DevTools Profiler
jsx
// 使用Profiler API
import { Profiler } from 'react';
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<MainContent />
</Profiler>
);
}
function onRenderCallback(
id, // Profiler的id
phase, // "mount" 或 "update"
actualDuration, // 本次渲染花费的时间
baseDuration, // 未优化情况下渲染所需时间
startTime, // React开始渲染的时间
commitTime, // React提交更新的时间
interactions // 触发此次渲染的交互集合
) {
console.log(`[Profiler] ${id} - ${phase}:`, {
actualDuration: `${actualDuration.toFixed(2)}ms`,
baseDuration: `${baseDuration.toFixed(2)}ms`,
efficiency: `${((1 - actualDuration / baseDuration) * 100).toFixed(2)}%`
});
// 记录慢渲染
if (actualDuration > 16) { // > 16ms会掉帧
console.warn(`[Slow Render] ${id} took ${actualDuration.toFixed(2)}ms`);
}
}
// 有条件地启用Profiler
function ConditionalProfiler({ children, enabled = process.env.NODE_ENV === 'development' }) {
if (enabled) {
return (
<Profiler id="ConditionalProfiler" onRender={onRenderCallback}>
{children}
</Profiler>
);
}
return children;
}2.2 Chrome DevTools Performance
typescript
const chromePerformanceGuide = {
使用步骤: [
'1. 打开Chrome DevTools -> Performance',
'2. 点击Record',
'3. 执行操作(如滚动、点击)',
'4. 停止录制',
'5. 分析火焰图'
],
关键区域: [
'Main线程: JavaScript执行和DOM操作',
'Compositor: 合成器线程',
'Raster: 光栅化',
'GPU: GPU操作'
],
识别问题: [
'长任务 (Long Tasks): 黄色块 > 50ms',
'强制同步布局 (Forced Reflow)',
'JavaScript执行时间过长',
'过多的样式计算',
'过多的布局重排'
]
};2.3 自定义性能监控
jsx
// 创建性能监控Hook
function usePerformanceMonitor(componentName, threshold = 16) {
const renderCountRef = useRef(0);
const slowRendersRef = useRef(0);
const renderTimesRef = useRef([]);
const startTime = useRef(performance.now());
useEffect(() => {
const endTime = performance.now();
const renderTime = endTime - startTime.current;
renderCountRef.current++;
renderTimesRef.current.push(renderTime);
if (renderTime > threshold) {
slowRendersRef.current++;
console.warn(
`[Slow Render] ${componentName}: ${renderTime.toFixed(2)}ms` +
` (${slowRendersRef.current}/${renderCountRef.current})`
);
}
// 每10次渲染报告一次统计
if (renderCountRef.current % 10 === 0) {
const times = renderTimesRef.current;
const avg = times.reduce((a, b) => a + b, 0) / times.length;
const max = Math.max(...times);
const min = Math.min(...times);
console.log(`[Performance] ${componentName}:`, {
renders: renderCountRef.current,
slowRenders: slowRendersRef.current,
avgTime: `${avg.toFixed(2)}ms`,
maxTime: `${max.toFixed(2)}ms`,
minTime: `${min.toFixed(2)}ms`
});
}
startTime.current = performance.now();
});
return {
renderCount: renderCountRef.current,
slowRenders: slowRendersRef.current
};
}
// 使用
function MonitoredComponent() {
const { renderCount, slowRenders } = usePerformanceMonitor('MonitoredComponent');
const [count, setCount] = useState(0);
// 模拟昂贵计算
const expensiveValue = useMemo(() => {
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += i;
}
return result;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Value: {expensiveValue}</p>
<p>Renders: {renderCount} (Slow: {slowRenders})</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}2.4 Why Did You Render
jsx
// 安装: npm install @welldone-software/why-did-you-render
// 配置 (在index.js最顶部)
import React from 'react';
if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
trackAllPureComponents: true,
trackHooks: true,
trackExtraHooks: [[require('react-redux/lib'), 'useSelector']],
logOnDifferentValues: true
});
}
// 使用: 在组件上添加标记
function MyComponent({ user }) {
return <div>{user.name}</div>;
}
MyComponent.whyDidYouRender = true;
// 或使用Hook
function useWhyDidYouUpdate(name, props) {
const previousProps = useRef();
useEffect(() => {
if (previousProps.current) {
const allKeys = Object.keys({ ...previousProps.current, ...props });
const changedProps = {};
allKeys.forEach(key => {
if (previousProps.current[key] !== props[key]) {
changedProps[key] = {
from: previousProps.current[key],
to: props[key]
};
}
});
if (Object.keys(changedProps).length > 0) {
console.log('[Why Update]', name, changedProps);
}
}
previousProps.current = props;
});
}
// 使用
function Component({ user, settings }) {
useWhyDidYouUpdate('Component', { user, settings });
return <div>{user.name}</div>;
}3. 常见性能问题
3.1 不必要的重渲染
jsx
// ❌ 问题: 父组件重渲染导致子组件重渲染
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
{/* text变化时,Child也会重渲染 */}
<ExpensiveChild count={count} />
</div>
);
}
function ExpensiveChild({ count }) {
console.log('ExpensiveChild rendered');
// 模拟昂贵渲染
const result = useMemo(() => {
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
return sum;
}, []);
return <div>Count: {count}, Result: {result}</div>;
}
// ✅ 解决方案1: React.memo
const MemoizedChild = React.memo(function ExpensiveChild({ count }) {
console.log('MemoizedChild rendered');
const result = useMemo(() => {
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
return sum;
}, []);
return <div>Count: {count}, Result: {result}</div>;
});
function ParentSolution1() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
{/* text变化时,MemoizedChild不会重渲染 */}
<MemoizedChild count={count} />
</div>
);
}
// ✅ 解决方案2: 状态下放
function ParentSolution2() {
const [count, setCount] = useState(0);
return (
<div>
<TextInput /> {/* 独立组件,不影响其他组件 */}
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
<ExpensiveChild count={count} />
</div>
);
}
function TextInput() {
const [text, setText] = useState('');
return <input value={text} onChange={e => setText(e.target.value)} />;
}
// ✅ 解决方案3: children prop
function ParentSolution3({ children }) {
const [text, setText] = useState('');
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
{/* children不会因为text变化而重渲染 */}
{children}
</div>
);
}
function App() {
const [count, setCount] = useState(0);
return (
<ParentSolution3>
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
<ExpensiveChild count={count} />
</ParentSolution3>
);
}3.2 内联对象和数组
jsx
// ❌ 问题: 每次渲染都创建新对象
function BadInlineObjects() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
{/* style对象每次都是新的,导致不必要的重渲染 */}
<ExpensiveComponent
style={{ color: 'red', fontSize: 16 }}
options={['option1', 'option2']}
/>
</div>
);
}
// ✅ 解决方案1: 提取到组件外部
const STYLES = { color: 'red', fontSize: 16 };
const OPTIONS = ['option1', 'option2'];
function GoodInlineObjectsSolution1() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ExpensiveComponent style={STYLES} options={OPTIONS} />
</div>
);
}
// ✅ 解决方案2: useMemo
function GoodInlineObjectsSolution2() {
const [count, setCount] = useState(0);
const style = useMemo(() => ({ color: 'red', fontSize: 16 }), []);
const options = useMemo(() => ['option1', 'option2'], []);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ExpensiveComponent style={style} options={options} />
</div>
);
}
// ✅ 解决方案3: 自定义比较
const ExpensiveComponent = React.memo(
function({ style, options }) {
console.log('ExpensiveComponent rendered');
return <div style={style}>{options.join(', ')}</div>;
},
(prevProps, nextProps) => {
// 自定义比较逻辑
return (
prevProps.style.color === nextProps.style.color &&
prevProps.style.fontSize === nextProps.style.fontSize &&
prevProps.options.length === nextProps.options.length &&
prevProps.options.every((opt, i) => opt === nextProps.options[i])
);
}
);3.3 内联函数
jsx
// ❌ 问题: 每次渲染都创建新函数
function BadInlineFunctions() {
const [items, setItems] = useState([1, 2, 3]);
return (
<div>
{items.map(item => (
/* 每个item的onClick都是新函数 */
<ExpensiveItem
key={item}
item={item}
onClick={() => console.log(item)}
/>
))}
</div>
);
}
// ✅ 解决方案1: useCallback
function GoodInlineFunctionsSolution1() {
const [items, setItems] = useState([1, 2, 3]);
const handleClick = useCallback((item) => {
console.log(item);
}, []);
return (
<div>
{items.map(item => (
<ExpensiveItem
key={item}
item={item}
onClick={handleClick}
/>
))}
</div>
);
}
// ExpensiveItem需要处理参数
const ExpensiveItem = React.memo(function({ item, onClick }) {
console.log('ExpensiveItem rendered:', item);
return <div onClick={() => onClick(item)}>{item}</div>;
});
// ✅ 解决方案2: 将状态下放到子组件
function GoodInlineFunctionsSolution2() {
const [items, setItems] = useState([1, 2, 3]);
return (
<div>
{items.map(item => (
<SelfContainedItem key={item} item={item} />
))}
</div>
);
}
function SelfContainedItem({ item }) {
const handleClick = useCallback(() => {
console.log(item);
}, [item]);
return <div onClick={handleClick}>{item}</div>;
}3.4 Context滥用
jsx
// ❌ 问题: Context value每次都是新对象
const AppContext = createContext();
function BadContextProvider({ children }) {
const [user, setUser] = useState({ name: 'John' });
const [theme, setTheme] = useState('light');
// value对象每次渲染都是新的!
return (
<AppContext.Provider value={{ user, setUser, theme, setTheme }}>
{children}
</AppContext.Provider>
);
}
// ✅ 解决方案1: useMemo缓存value
function GoodContextProviderSolution1({ children }) {
const [user, setUser] = useState({ name: 'John' });
const [theme, setTheme] = useState('light');
const value = useMemo(() => ({
user,
setUser,
theme,
setTheme
}), [user, theme]);
return (
<AppContext.Provider value={value}>
{children}
</AppContext.Provider>
);
}
// ✅ 解决方案2: 分离Context
const UserContext = createContext();
const ThemeContext = createContext();
function GoodContextProviderSolution2({ children }) {
const [user, setUser] = useState({ name: 'John' });
const [theme, setTheme] = useState('light');
const userValue = useMemo(() => ({ user, setUser }), [user]);
const themeValue = useMemo(() => ({ theme, setTheme }), [theme]);
return (
<UserContext.Provider value={userValue}>
<ThemeContext.Provider value={themeValue}>
{children}
</ThemeContext.Provider>
</UserContext.Provider>
);
}
// 消费者只订阅需要的Context
function UserDisplay() {
const { user } = useContext(UserContext); // 只在user变化时重渲染
return <div>{user.name}</div>;
}
function ThemeDisplay() {
const { theme } = useContext(ThemeContext); // 只在theme变化时重渲染
return <div>Theme: {theme}</div>;
}
// ✅ 解决方案3: Context选择器
function createContextSelector(Context) {
return function useContextSelector(selector) {
const value = useContext(Context);
const selectedValue = selector(value);
const selectedRef = useRef(selectedValue);
if (selectedRef.current !== selectedValue) {
selectedRef.current = selectedValue;
}
return selectedRef.current;
};
}
const useAppContextSelector = createContextSelector(AppContext);
function OptimizedComponent() {
// 只在user.name变化时重渲染
const userName = useAppContextSelector(state => state.user.name);
return <div>{userName}</div>;
}4. 大列表优化
4.1 虚拟滚动
jsx
// 简单的虚拟列表实现
function VirtualList({ items, itemHeight, containerHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const totalHeight = items.length * itemHeight;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight) + 1,
items.length
);
const visibleItems = items.slice(startIndex, endIndex);
const offsetY = startIndex * itemHeight;
const handleScroll = (e) => {
setScrollTop(e.target.scrollTop);
};
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={handleScroll}
>
<div style={{ height: totalHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map((item, index) => (
<div
key={startIndex + index}
style={{ height: itemHeight }}
>
{item}
</div>
))}
</div>
</div>
</div>
);
}
// 使用react-window库 (推荐)
import { FixedSizeList } from 'react-window';
function OptimizedVirtualList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index]}
</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// 动态高度虚拟列表
import { VariableSizeList } from 'react-window';
function DynamicVirtualList({ items }) {
const listRef = useRef();
const rowHeights = useRef({});
const getItemSize = (index) => {
return rowHeights.current[index] || 50;
};
const setRowHeight = (index, size) => {
listRef.current.resetAfterIndex(0);
rowHeights.current = { ...rowHeights.current, [index]: size };
};
const Row = ({ index, style }) => {
const rowRef = useRef();
useEffect(() => {
if (rowRef.current) {
setRowHeight(index, rowRef.current.clientHeight);
}
}, [index]);
return (
<div style={style}>
<div ref={rowRef}>
{items[index]}
</div>
</div>
);
};
return (
<VariableSizeList
ref={listRef}
height={600}
itemCount={items.length}
itemSize={getItemSize}
width="100%"
>
{Row}
</VariableSizeList>
);
}4.2 分页和无限滚动
jsx
// 分页
function PaginatedList({ items, pageSize = 20 }) {
const [currentPage, setCurrentPage] = useState(1);
const totalPages = Math.ceil(items.length / pageSize);
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const currentItems = items.slice(startIndex, endIndex);
return (
<div>
<div>
{currentItems.map((item, index) => (
<div key={startIndex + index}>{item}</div>
))}
</div>
<div>
<button
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
disabled={currentPage === 1}
>
Previous
</button>
<span>Page {currentPage} of {totalPages}</span>
<button
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
disabled={currentPage === totalPages}
>
Next
</button>
</div>
</div>
);
}
// 无限滚动
function InfiniteScrollList({ loadMore, hasMore }) {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const observerRef = useRef();
const loaderRef = useRef();
useEffect(() => {
const options = {
root: null,
rootMargin: '100px',
threshold: 0
};
observerRef.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore && !loading) {
setLoading(true);
loadMore().then(newItems => {
setItems(prev => [...prev, ...newItems]);
setLoading(false);
});
}
}, options);
if (loaderRef.current) {
observerRef.current.observe(loaderRef.current);
}
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, [loadMore, hasMore, loading]);
return (
<div>
{items.map((item, index) => (
<div key={index}>{item}</div>
))}
{hasMore && <div ref={loaderRef}>Loading...</div>}
</div>
);
}4.3 列表项优化
jsx
// ❌ 低效的列表渲染
function BadList({ items, onItemClick }) {
return (
<div>
{items.map(item => (
<div key={item.id} onClick={() => onItemClick(item)}>
{item.name} - {item.description}
</div>
))}
</div>
);
}
// ✅ 优化的列表渲染
function GoodList({ items, onItemClick }) {
const handleClick = useCallback((item) => {
onItemClick(item);
}, [onItemClick]);
return (
<div>
{items.map(item => (
<ListItem key={item.id} item={item} onClick={handleClick} />
))}
</div>
);
}
const ListItem = React.memo(function({ item, onClick }) {
const handleClick = useCallback(() => {
onClick(item);
}, [item, onClick]);
return (
<div onClick={handleClick}>
{item.name} - {item.description}
</div>
);
});
// ✅ 更进一步: 复杂列表项
const ComplexListItem = React.memo(
function({ item, onClick }) {
const handleClick = useCallback(() => {
onClick(item);
}, [item, onClick]);
// 昂贵的计算缓存
const processedData = useMemo(() => {
return processItemData(item);
}, [item]);
return (
<div onClick={handleClick}>
<h3>{item.name}</h3>
<p>{item.description}</p>
<ComplexVisualization data={processedData} />
</div>
);
},
(prevProps, nextProps) => {
// 自定义比较
return (
prevProps.item.id === nextProps.item.id &&
prevProps.item.updatedAt === nextProps.item.updatedAt &&
prevProps.onClick === nextProps.onClick
);
}
);5. 高级优化技巧
5.1 代码分割和懒加载
jsx
// 路由级别的代码分割
import { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</Router>
);
}
// 组件级别的代码分割
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function Parent() {
const [showHeavy, setShowHeavy] = useState(false);
return (
<div>
<button onClick={() => setShowHeavy(true)}>Show Heavy Component</button>
{showHeavy && (
<Suspense fallback={<div>Loading heavy component...</div>}>
<HeavyComponent />
</Suspense>
)}
</div>
);
}
// 预加载策略
function ComponentWithPreload() {
const handleMouseEnter = () => {
// 鼠标悬停时预加载
import('./HeavyComponent');
};
return (
<button onMouseEnter={handleMouseEnter}>
Load Heavy Component
</button>
);
}5.2 并发特性
jsx
// useTransition: 标记非紧急更新
import { useTransition, useState } from 'react';
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value); // 紧急更新
// 非紧急更新
startTransition(() => {
const searchResults = performExpensiveSearch(value);
setResults(searchResults);
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>Searching...</div>}
<div>
{results.map(result => (
<div key={result.id}>{result.name}</div>
))}
</div>
</div>
);
}
// useDeferredValue: 延迟值的更新
import { useDeferredValue, useMemo } from 'react';
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => {
return performExpensiveSearch(deferredQuery);
}, [deferredQuery]);
return (
<div>
{results.map(result => (
<div key={result.id}>{result.name}</div>
))}
</div>
);
}
function SearchApp() {
const [query, setQuery] = useState('');
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<SearchResults query={query} />
</div>
);
}5.3 批量更新
jsx
// React 18自动批量更新
function AutoBatchingExample() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
const handleClick = () => {
// React 18中,这些更新会自动批量处理
setCount(c => c + 1);
setFlag(f => !f);
// 只会触发一次重渲染
};
const handleAsyncClick = async () => {
await fetch('/api/data');
// React 18中,即使在异步回调中也会批量更新
setCount(c => c + 1);
setFlag(f => !f);
// 只会触发一次重渲染
};
return (
<div>
<button onClick={handleClick}>Sync Update</button>
<button onClick={handleAsyncClick}>Async Update</button>
<p>Count: {count}, Flag: {flag.toString()}</p>
</div>
);
}
// 手动退出批量更新 (如果需要)
import { flushSync } from 'react-dom';
function ManualBatchingControl() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
const handleClick = () => {
flushSync(() => {
setCount(c => c + 1); // 立即渲染
});
flushSync(() => {
setFlag(f => !f); // 再次渲染
});
// 会触发两次重渲染
};
return (
<div>
<button onClick={handleClick}>Update</button>
<p>Count: {count}, Flag: {flag.toString()}</p>
</div>
);
}6. 性能优化清单
typescript
const performanceOptimizationChecklist = {
组件优化: [
'✅ 使用React.memo避免不必要的重渲染',
'✅ 使用useMemo缓存昂贵计算',
'✅ 使用useCallback稳定函数引用',
'✅ 避免内联对象和数组',
'✅ 状态下放到需要的组件',
'✅ 使用children prop模式'
],
列表优化: [
'✅ 使用虚拟滚动(react-window)',
'✅ 实现分页或无限滚动',
'✅ 优化列表项渲染',
'✅ 使用稳定的key',
'✅ 避免在map中使用内联函数'
],
Context优化: [
'✅ 使用useMemo缓存Context value',
'✅ 拆分Context减小影响范围',
'✅ 使用Context选择器',
'✅ 考虑使用状态管理库'
],
代码分割: [
'✅ 路由级别懒加载',
'✅ 组件级别懒加载',
'✅ 实现预加载策略',
'✅ 使用动态import'
],
并发特性: [
'✅ 使用useTransition标记非紧急更新',
'✅ 使用useDeferredValue延迟值更新',
'✅ 利用自动批量更新',
'✅ 合理使用Suspense'
],
监控和测试: [
'✅ 使用React DevTools Profiler',
'✅ 使用Chrome Performance工具',
'✅ 实现自定义性能监控',
'✅ 使用Why Did You Render',
'✅ 编写性能测试'
]
};7. 总结
渲染性能优化是React应用开发的重要环节:
- 识别问题: React DevTools Profiler、Chrome Performance、自定义监控
- 常见问题: 不必要重渲染、内联对象/函数、Context滥用、大列表
- 优化策略: React.memo、useMemo/useCallback、虚拟滚动、代码分割
- 高级技巧: 并发特性、批量更新、预加载
- 持续监控: 性能指标、自动化测试、用户体验
通过系统地应用这些技巧,可以构建高性能的React应用,提供流畅的用户体验。