Appearance
常见性能问题与解决 - React性能问题诊断手册
1. 渲染性能问题
1.1 不必要的重渲染
typescript
const unnecessaryRerender = {
问题1_父组件导致: `
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
<ExpensiveChild /> {/* count变化时也重渲染 */}
</div>
);
}
function ExpensiveChild() {
console.log('ExpensiveChild render');
return <div>{heavyComputation()}</div>;
}
`,
解决方案: `
// 方案1: 使用React.memo
const ExpensiveChild = React.memo(function ExpensiveChild() {
console.log('ExpensiveChild render');
return <div>{heavyComputation()}</div>;
});
// 方案2: 状态下放
function Parent() {
return (
<div>
<Counter />
<ExpensiveChild />
</div>
);
}
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// 方案3: children prop
function Parent({ children }) {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
{children} {/* children不会重新创建 */}
</div>
);
}
<Parent>
<ExpensiveChild />
</Parent>
`,
问题2_内联对象: `
function Parent() {
return (
<Child
style={{ color: 'red' }} {/* 每次都是新对象 */}
config={{ theme: 'dark' }} {/* 每次都是新对象 */}
/>
);
}
const Child = React.memo(({ style, config }) => {
console.log('Child render'); // 每次都渲染
return <div style={style}>{config.theme}</div>;
});
`,
解决: `
// 提取到组件外
const style = { color: 'red' };
const config = { theme: 'dark' };
function Parent() {
return <Child style={style} config={config} />;
}
// 或使用useMemo
function Parent() {
const style = useMemo(() => ({ color: 'red' }), []);
const config = useMemo(() => ({ theme: 'dark' }), []);
return <Child style={style} config={config} />;
}
`
};1.2 大列表性能问题
typescript
const largeListProblems = {
问题: `
function List({ items }) { // items有10000项
return (
<ul>
{items.map(item => (
<li key={item.id}>
<div>{item.name}</div>
<div>{item.description}</div>
<button onClick={() => handleClick(item.id)}>Action</button>
</li>
))}
</ul>
);
}
// 问题:
// - 渲染10000个DOM节点
// - 初始渲染慢
// - 滚动卡顿
// - 内存占用大
`,
解决方案1_虚拟滚动: `
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
const Row = ({ index, style }) => {
const item = items[index];
return (
<div style={style}>
<div>{item.name}</div>
<div>{item.description}</div>
<button onClick={() => handleClick(item.id)}>Action</button>
</div>
);
};
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={80}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// 性能提升:
// - 只渲染可见区域(约10-15项)
// - 滚动流畅
// - 内存占用低
`,
解决方案2_分页: `
function PaginatedList({ items }) {
const [page, setPage] = useState(1);
const pageSize = 20;
const paginatedItems = useMemo(() => {
const start = (page - 1) * pageSize;
return items.slice(start, start + pageSize);
}, [items, page]);
return (
<div>
<ul>
{paginatedItems.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
<Pagination
page={page}
total={Math.ceil(items.length / pageSize)}
onChange={setPage}
/>
</div>
);
}
`,
解决方案3_无限滚动: `
function InfiniteList() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const loadMore = useCallback(async () => {
const newItems = await fetchItems(page);
setItems(prev => [...prev, ...newItems]);
setPage(p => p + 1);
setHasMore(newItems.length > 0);
}, [page]);
return (
<InfiniteScroll
dataLength={items.length}
next={loadMore}
hasMore={hasMore}
loader={<Loading />}
>
{items.map(item => (
<Item key={item.id} item={item} />
))}
</InfiniteScroll>
);
}
`
};1.3 Context导致的重渲染
typescript
const contextRerender = {
问题: `
const AppContext = createContext();
function App() {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [count, setCount] = useState(0);
// ❌ value每次都是新对象
const value = { user, setUser, theme, setTheme, count, setCount };
return (
<AppContext.Provider value={value}>
<Page />
</AppContext.Provider>
);
}
function Page() {
const { theme } = useContext(AppContext);
// count变化也会导致Page重渲染,即使只用了theme
return <div className={theme}>Content</div>;
}
`,
解决方案1_分离Context: `
const UserContext = createContext();
const ThemeContext = createContext();
const CountContext = createContext();
function App() {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [count, setCount] = useState(0);
return (
<UserContext.Provider value={{ user, setUser }}>
<ThemeContext.Provider value={{ theme, setTheme }}>
<CountContext.Provider value={{ count, setCount }}>
<Page />
</CountContext.Provider>
</ThemeContext.Provider>
</UserContext.Provider>
);
}
function Page() {
const { theme } = useContext(ThemeContext);
// 只有theme变化才重渲染
return <div className={theme}>Content</div>;
}
`,
解决方案2_useMemo: `
function App() {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const value = useMemo(
() => ({ user, setUser, theme, setTheme }),
[user, theme]
);
return (
<AppContext.Provider value={value}>
<Page />
</AppContext.Provider>
);
}
`,
解决方案3_状态管理库: `
// 使用Zustand
const useStore = create((set) => ({
user: null,
theme: 'light',
count: 0,
setUser: (user) => set({ user }),
setTheme: (theme) => set({ theme }),
setCount: (count) => set({ count })
}));
function Page() {
const theme = useStore(state => state.theme);
// 只订阅theme,其他状态变化不影响
return <div className={theme}>Content</div>;
}
`
};2. 内存泄漏问题
2.1 事件监听器未清理
typescript
const eventListenerLeak = {
问题: `
function Component() {
useEffect(() => {
const handler = () => console.log('resize');
window.addEventListener('resize', handler);
// ❌ 忘记清理
}, []);
return <div>Component</div>;
}
// 组件卸载后,监听器仍然存在
`,
解决: `
function Component() {
useEffect(() => {
const handler = () => console.log('resize');
window.addEventListener('resize', handler);
// ✓ 清理监听器
return () => {
window.removeEventListener('resize', handler);
};
}, []);
return <div>Component</div>;
}
`
};2.2 定时器未清理
typescript
const timerLeak = {
问题: `
function Component() {
useEffect(() => {
const timer = setInterval(() => {
console.log('tick');
}, 1000);
// ❌ 未清理定时器
}, []);
}
`,
解决: `
function Component() {
useEffect(() => {
const timer = setInterval(() => {
console.log('tick');
}, 1000);
// ✓ 清理定时器
return () => clearInterval(timer);
}, []);
}
`
};2.3 异步操作未取消
typescript
const asyncLeak = {
问题: `
function Component({ id }) {
const [data, setData] = useState(null);
useEffect(() => {
fetchData(id).then(result => {
setData(result); // 组件卸载后仍然执行
});
}, [id]);
}
`,
解决: `
function Component({ id }) {
const [data, setData] = useState(null);
useEffect(() => {
let cancelled = false;
fetchData(id).then(result => {
if (!cancelled) {
setData(result);
}
});
return () => {
cancelled = true;
};
}, [id]);
}
// 或使用AbortController
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
return () => controller.abort();
}, [url]);
`
};3. 状态更新问题
3.1 批量更新失效
typescript
const batchingIssues = {
React17问题: `
// React 17: setTimeout中不批量
function Component() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
const handleClick = () => {
setTimeout(() => {
setCount(c => c + 1); // 触发渲染1
setFlag(f => !f); // 触发渲染2
}, 0);
};
// 触发2次渲染
}
`,
React17解决: `
import { unstable_batchedUpdates } from 'react-dom';
const handleClick = () => {
setTimeout(() => {
unstable_batchedUpdates(() => {
setCount(c => c + 1);
setFlag(f => !f);
});
}, 0);
// 只触发1次渲染
};
`,
React18自动: `
// React 18: 自动批量所有更新
const handleClick = () => {
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
}, 0);
// 自动批量,只触发1次渲染
};
`
};3.2 闭包陷阱
typescript
const closureTrap = {
问题: `
function Component() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // count永远是0
}, 1000);
return () => clearInterval(timer);
}, []); // 空依赖,闭包捕获初始count
// count只会变成1,不会继续增加
}
`,
解决: `
// 方案1: 函数式更新
useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1); // 基于最新值
}, 1000);
return () => clearInterval(timer);
}, []);
// 方案2: 添加依赖
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(timer);
}, [count]); // count变化时重建定时器
// 方案3: useRef
const countRef = useRef(count);
countRef.current = count;
useEffect(() => {
const timer = setInterval(() => {
setCount(countRef.current + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
`
};4. 网络性能问题
4.1 Bundle过大
typescript
const bundleSize = {
诊断: `
// webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer
// package.json
"analyze": "webpack-bundle-analyzer dist/stats.json"
`,
常见原因: [
'导入整个库(如lodash)',
'未使用的依赖',
'重复打包',
'未压缩',
'未tree shaking'
],
优化: `
// 1. 按需导入
// ❌ import _ from 'lodash';
// ✓ import debounce from 'lodash/debounce';
// 2. 代码分割
const Heavy = lazy(() => import('./Heavy'));
// 3. 动态导入
const loadModule = async () => {
const mod = await import('./module');
return mod.default;
};
// 4. 替换大型库
// moment.js (200KB) -> day.js (2KB)
// lodash -> lodash-es (支持tree shaking)
`
};4.2 过多HTTP请求
typescript
const tooManyRequests = {
问题: `
function Component() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
useEffect(() => {
fetchUser().then(setUser); // 请求1
fetchPosts().then(setPosts); // 请求2
fetchComments().then(setComments); // 请求3
}, []);
// 串行请求,慢
}
`,
解决_并行请求: `
useEffect(() => {
Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]).then(([user, posts, comments]) => {
setUser(user);
setPosts(posts);
setComments(comments);
});
}, []);
`,
解决_GraphQL: `
// 一次请求获取所有数据
const query = gql\`
query PageData {
user { id name }
posts { id title }
comments { id content }
}
\`;
const { data } = useQuery(query);
`,
解决_BFF: `
// Backend For Frontend
// 后端聚合数据
const data = await fetch('/api/page-data');
// 一次请求返回所有数据
`
};5. 诊断工具
5.1 React DevTools Profiler
typescript
const profilerUsage = {
记录性能: `
1. 打开React DevTools
2. 切换到Profiler标签
3. 点击录制按钮
4. 执行操作
5. 停止录制
6. 分析结果
`,
分析指标: {
渲染时长: '组件渲染花费的时间',
渲染原因: 'Why did this render?',
提交时长: 'commit阶段花费的时间',
组件树: '查看组件渲染层级'
},
找出问题: [
'渲染时间过长的组件',
'频繁重渲染的组件',
'不必要的props更新',
'昂贵的计算'
]
};5.2 Chrome DevTools
typescript
const chromeDevTools = {
Performance面板: `
1. 打开DevTools
2. 切换到Performance
3. 录制
4. 执行操作
5. 停止
6. 分析火焰图
`,
查看: [
'JavaScript执行时间',
'渲染时间',
'布局计算',
'重绘次数',
'长任务(Long Tasks)'
],
优化建议: [
'减少JavaScript执行时间',
'避免强制同步布局',
'减少重绘重排',
'拆分长任务'
]
};6. 面试高频问题
typescript
const interviewQA = {
Q1: {
question: 'React性能问题常见原因?',
answer: [
'1. 不必要的重渲染',
'2. 大列表渲染',
'3. Bundle过大',
'4. 内存泄漏',
'5. Context滥用',
'6. 未优化的图片',
'7. 过多HTTP请求'
]
},
Q2: {
question: '如何诊断性能问题?',
answer: [
'1. React DevTools Profiler',
'2. Chrome DevTools Performance',
'3. Lighthouse审计',
'4. Web Vitals监控',
'5. Bundle分析器',
'6. 性能监控工具(Sentry等)'
]
},
Q3: {
question: '如何避免不必要的重渲染?',
answer: [
'1. 使用React.memo',
'2. 使用useMemo/useCallback',
'3. 状态下放',
'4. 组件拆分',
'5. children prop模式',
'6. 避免内联对象/函数'
]
},
Q4: {
question: '内存泄漏如何排查和修复?',
answer: [
'1. Chrome DevTools Memory面板',
'2. 检查事件监听器',
'3. 检查定时器',
'4. 检查异步操作',
'5. useEffect返回清理函数',
'6. 取消未完成的请求'
]
}
};7. 总结
常见性能问题与解决的核心要点:
- 重渲染: memo/useMemo/useCallback/组件拆分
- 大列表: 虚拟滚动/分页/无限滚动
- Context: 分离/useMemo/状态管理库
- 内存泄漏: 清理监听器/定时器/异步
- Bundle: 代码分割/tree shaking/按需导入
- 诊断: DevTools/Profiler/Lighthouse
掌握这些解决方案可以有效解决React性能问题。
8. 高级性能优化技巧
8.1 组件懒加载策略
jsx
// 基础懒加载
const HeavyComponent = lazy(() => import('./HeavyComponent'));
// 预加载策略
const HeavyComponent = lazy(() => import('./HeavyComponent'));
// 预加载函数
const preload = () => {
const component = import('./HeavyComponent');
return component;
};
// 在适当时机预加载
function App() {
return (
<div
onMouseEnter={preload} // 鼠标悬停时预加载
>
<Link to="/heavy">进入页面</Link>
</div>
);
}
// 带超时的懒加载
function lazyWithTimeout(importFn, timeout = 5000) {
return lazy(() => {
return Promise.race([
importFn(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
});
}
const TimeoutComponent = lazyWithTimeout(
() => import('./SlowComponent'),
3000
);8.2 智能缓存策略
jsx
// LRU缓存实现
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return null;
const value = this.cache.get(key);
// 移到最新位置
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 删除最旧的
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
}
// React中使用LRU缓存
function useDataCache(capacity = 10) {
const cache = useRef(new LRUCache(capacity));
const fetchWithCache = useCallback(async (key, fetchFn) => {
const cached = cache.current.get(key);
if (cached) return cached;
const data = await fetchFn();
cache.current.set(key, data);
return data;
}, []);
return fetchWithCache;
}
// 使用示例
function DataComponent({ userId }) {
const [data, setData] = useState(null);
const fetchWithCache = useDataCache(20);
useEffect(() => {
fetchWithCache(userId, () => fetchUserData(userId))
.then(setData);
}, [userId, fetchWithCache]);
return <div>{data?.name}</div>;
}8.3 Web Worker优化
jsx
// worker.js
self.addEventListener('message', (e) => {
const { type, data } = e.data;
if (type === 'PROCESS_DATA') {
// 昂贵的计算
const result = processLargeData(data);
self.postMessage({ type: 'RESULT', data: result });
}
});
function processLargeData(data) {
// 复杂计算逻辑
return data.map(item => {
// 耗时操作
return complexCalculation(item);
});
}
// React组件使用Worker
function useWorker(workerPath) {
const workerRef = useRef(null);
useEffect(() => {
workerRef.current = new Worker(workerPath);
return () => {
workerRef.current?.terminate();
};
}, [workerPath]);
const postMessage = useCallback((message) => {
return new Promise((resolve) => {
const handleMessage = (e) => {
workerRef.current.removeEventListener('message', handleMessage);
resolve(e.data);
};
workerRef.current.addEventListener('message', handleMessage);
workerRef.current.postMessage(message);
});
}, []);
return postMessage;
}
// 使用示例
function DataProcessor({ data }) {
const [result, setResult] = useState(null);
const postMessage = useWorker('/worker.js');
useEffect(() => {
postMessage({ type: 'PROCESS_DATA', data })
.then(response => setResult(response.data));
}, [data, postMessage]);
return <div>{result}</div>;
}8.4 图片优化策略
jsx
// 渐进式图片加载
function ProgressiveImage({ lowQualitySrc, highQualitySrc, alt }) {
const [src, setSrc] = useState(lowQualitySrc);
const [loading, setLoading] = useState(true);
useEffect(() => {
const img = new Image();
img.src = highQualitySrc;
img.onload = () => {
setSrc(highQualitySrc);
setLoading(false);
};
}, [highQualitySrc]);
return (
<img
src={src}
alt={alt}
style={{
filter: loading ? 'blur(10px)' : 'none',
transition: 'filter 0.3s'
}}
/>
);
}
// 懒加载图片
function LazyImage({ src, alt, placeholder }) {
const [imageSrc, setImageSrc] = useState(placeholder);
const [imageRef, setImageRef] = useState();
useEffect(() => {
let observer;
if (imageRef && imageSrc === placeholder) {
observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setImageSrc(src);
observer.unobserve(imageRef);
}
});
},
{ rootMargin: '100px' }
);
observer.observe(imageRef);
}
return () => {
if (observer && imageRef) {
observer.unobserve(imageRef);
}
};
}, [src, imageSrc, imageRef, placeholder]);
return <img ref={setImageRef} src={imageSrc} alt={alt} />;
}
// WebP支持检测
function useWebPSupport() {
const [supportsWebP, setSupportsWebP] = useState(false);
useEffect(() => {
const img = new Image();
img.onload = () => setSupportsWebP(img.width === 1);
img.onerror = () => setSupportsWebP(false);
img.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=';
}, []);
return supportsWebP;
}
// 智能图片组件
function SmartImage({ src, alt }) {
const supportsWebP = useWebPSupport();
const imageSrc = supportsWebP
? src.replace(/\.(jpg|png)$/, '.webp')
: src;
return <LazyImage src={imageSrc} alt={alt} placeholder="/placeholder.jpg" />;
}9. 实战优化案例
9.1 电商产品列表优化
jsx
// 问题:10000个产品,严重卡顿
// 解决方案1:虚拟滚动 + 图片懒加载
import { FixedSizeList } from 'react-window';
function ProductList({ products }) {
const Row = useCallback(({ index, style }) => {
const product = products[index];
return (
<div style={style}>
<ProductCard product={product} />
</div>
);
}, [products]);
return (
<FixedSizeList
height={600}
itemCount={products.length}
itemSize={120}
width="100%"
>
{Row}
</FixedSizeList>
);
}
const ProductCard = memo(({ product }) => {
return (
<div className="product-card">
<LazyImage src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.price}</p>
</div>
);
});
// 性能提升:
// - 首次渲染:从3000ms降到150ms
// - 滚动FPS:从15提升到60
// - 内存占用:从800MB降到120MB9.2 复杂表单优化
jsx
// 问题:100个字段的表单,输入延迟严重
// 解决方案:字段级更新 + 虚拟化
function ComplexForm({ initialData, onSubmit }) {
const [formData, setFormData] = useState(initialData);
// 字段级更新,避免整个表单重渲染
const updateField = useCallback((fieldName, value) => {
setFormData(prev => ({
...prev,
[fieldName]: value
}));
}, []);
return (
<form onSubmit={onSubmit}>
{Object.keys(formData).map(fieldName => (
<FormField
key={fieldName}
name={fieldName}
value={formData[fieldName]}
onChange={updateField}
/>
))}
</form>
);
}
const FormField = memo(({ name, value, onChange }) => {
// 防抖输入
const debouncedOnChange = useMemo(
() => debounce((name, value) => onChange(name, value), 300),
[onChange]
);
const handleChange = (e) => {
debouncedOnChange(name, e.target.value);
};
return (
<div>
<label>{name}</label>
<input
defaultValue={value}
onChange={handleChange}
/>
</div>
);
});
// 性能提升:
// - 输入延迟:从200ms降到16ms
// - 每次输入重渲染次数:从100降到19.3 实时数据看板优化
jsx
// 问题:实时更新的数据看板,每秒100次更新
// 解决方案:批量更新 + 选择性渲染
function Dashboard() {
const [metrics, setMetrics] = useState({});
useEffect(() => {
const ws = new WebSocket('wss://api.example.com');
const buffer = [];
let timeoutId;
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
buffer.push(data);
// 批量更新,每50ms合并一次
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
setMetrics(prev => {
const newMetrics = { ...prev };
buffer.forEach(item => {
newMetrics[item.id] = item;
});
buffer.length = 0;
return newMetrics;
});
}, 50);
};
return () => {
clearTimeout(timeoutId);
ws.close();
};
}, []);
return (
<div className="dashboard">
{Object.values(metrics).map(metric => (
<MetricCard key={metric.id} metric={metric} />
))}
</div>
);
}
const MetricCard = memo(({ metric }) => {
// 只在值真正变化时渲染
return (
<div className="metric-card">
<h3>{metric.name}</h3>
<p>{metric.value}</p>
</div>
);
}, (prev, next) => {
// 自定义比较函数
return prev.metric.value === next.metric.value;
});
// 性能提升:
// - 更新频率:从100次/秒降到20次/秒
// - CPU使用率:从80%降到15%
// - 渲染次数:减少85%10. 性能监控与分析
10.1 自定义性能监控
jsx
// 性能指标收集
function usePerformanceMonitor(componentName) {
const renderCount = useRef(0);
const renderTimes = useRef([]);
useEffect(() => {
renderCount.current += 1;
});
useEffect(() => {
const startTime = performance.now();
return () => {
const endTime = performance.now();
const renderTime = endTime - startTime;
renderTimes.current.push(renderTime);
// 每10次渲染报告一次
if (renderCount.current % 10 === 0) {
const avgTime = renderTimes.current.reduce((a, b) => a + b, 0)
/ renderTimes.current.length;
console.log(`[性能] ${componentName}:`, {
renders: renderCount.current,
avgRenderTime: avgTime.toFixed(2) + 'ms',
lastRenderTime: renderTime.toFixed(2) + 'ms'
});
renderTimes.current = [];
}
};
});
}
// 使用示例
function HeavyComponent(props) {
usePerformanceMonitor('HeavyComponent');
// 组件逻辑...
return <div>...</div>;
}10.2 React Profiler API
jsx
import { Profiler } from 'react';
function onRenderCallback(
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
interactions
) {
// 发送到分析服务
analytics.track('component_render', {
component: id,
phase,
actualDuration,
baseDuration
});
// 性能警告
if (actualDuration > 16) {
console.warn(`[性能警告] ${id} 渲染耗时 ${actualDuration}ms`);
}
}
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<Routes />
</Profiler>
);
}10.3 Web Vitals监控
jsx
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function reportWebVitals() {
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
}
function sendToAnalytics({ name, value, id }) {
// 发送到监控服务
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify({
metric: name,
value,
id,
timestamp: Date.now()
})
});
}
// 在应用入口调用
reportWebVitals();11. 面试重点总结
11.1 性能优化面试题
typescript
const interviewQuestions = [
{
q: '如何诊断React应用的性能问题?',
a: `
1. React DevTools Profiler:查看组件渲染时间
2. Chrome DevTools Performance:分析主线程活动
3. Lighthouse:综合性能评分
4. Why Did You Render:找出不必要的渲染
5. Bundle Analyzer:分析打包体积
`
},
{
q: '列举5种常见的性能优化手段',
a: `
1. React.memo:避免不必要的组件渲染
2. useMemo/useCallback:缓存计算结果和函数
3. 虚拟滚动:处理大列表
4. 代码分割:按需加载
5. 图片懒加载:延迟加载非关键资源
`
},
{
q: '什么情况下使用useMemo?',
a: `
1. 昂贵的计算(如数组排序、过滤)
2. 传递给子组件的对象/数组
3. 依赖项变化频率低
4. 注意:不要过度使用,有内存开销
`
},
{
q: 'Context性能问题如何解决?',
a: `
1. 拆分Context:按更新频率分离
2. 使用useMemo包装value
3. 考虑使用状态管理库(Redux/Zustand)
4. Context Selector模式
`
},
{
q: '虚拟滚动的实现原理?',
a: `
1. 只渲染可见区域的元素
2. 计算滚动位置,动态更新显示范围
3. 使用占位元素保持滚动条高度
4. 推荐库:react-window、react-virtuoso
`
}
];11.2 实战优化检查清单
typescript
const optimizationChecklist = {
'渲染优化': [
'使用React.memo包裹纯展示组件',
'使用useMemo缓存昂贵计算',
'使用useCallback稳定函数引用',
'避免在render中创建新对象/数组',
'合理拆分组件,控制渲染范围'
],
'列表优化': [
'为列表项添加稳定的key',
'大列表使用虚拟滚动',
'分页或无限滚动加载数据',
'列表项使用memo避免重渲染'
],
'资源优化': [
'使用代码分割和懒加载',
'图片使用懒加载和WebP格式',
'压缩和tree shaking',
'CDN加速静态资源',
'启用Gzip/Brotli压缩'
],
'状态优化': [
'避免不必要的全局状态',
'Context按更新频率拆分',
'大状态考虑useReducer',
'合理使用状态管理库'
],
'内存优化': [
'清理定时器和监听器',
'取消未完成的异步请求',
'避免内存泄漏',
'合理使用缓存'
]
};12. 性能优化最佳实践
12.1 开发阶段
typescript
// 1. 使用严格模式
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// 2. 启用性能监控
if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
trackAllPureComponents: true,
});
}
// 3. 使用ESLint规则
{
"extends": [
"plugin:react-hooks/recommended"
],
"rules": {
"react-hooks/exhaustive-deps": "error"
}
}12.2 构建阶段
javascript
// vite.config.js
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'router': ['react-router-dom'],
'ui': ['antd']
}
}
},
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
};12.3 运行时监控
jsx
// 错误边界 + 性能监控
class ErrorBoundary extends React.Component {
componentDidCatch(error, info) {
// 发送错误信息
analytics.trackError(error, info);
}
render() {
return this.props.children;
}
}
// 使用
<ErrorBoundary>
<Profiler id="App" onRender={onRenderCallback}>
<App />
</Profiler>
</ErrorBoundary>