react-performance
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Performance Optimization Skill
React性能优化技能
Overview
概述
Master React performance optimization techniques including memoization, code splitting, lazy loading, and profiling tools.
掌握React性能优化技术,包括记忆化、代码分割、懒加载和性能分析工具。
Learning Objectives
学习目标
- Identify performance bottlenecks
- Use React DevTools Profiler
- Implement memoization strategies
- Apply code splitting techniques
- Optimize bundle size
- 识别性能瓶颈
- 使用React DevTools Profiler
- 实现记忆化策略
- 应用代码分割技术
- 优化包体积
React DevTools Profiler
React DevTools Profiler
Profiling Steps
性能分析步骤
- Open React DevTools
- Go to Profiler tab
- Click record button
- Interact with app
- Stop recording
- Analyze flame graph
- 打开React DevTools
- 切换到Profiler标签页
- 点击录制按钮
- 与应用交互
- 停止录制
- 分析火焰图
Reading Results
结果解读
- Render duration: Time spent rendering
- Commits: Number of renders
- Flame graph: Visual render tree
- Ranked chart: Components by render time
- 渲染时长:渲染所花费的时间
- 提交次数:渲染的次数
- 火焰图:可视化的渲染树
- 排名图表:按渲染时间排序的组件
Memoization
记忆化
React.memo
React.memo
jsx
// Prevent re-render if props unchanged
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
return <div>{data.value}</div>;
});
// Custom comparison
const Component = React.memo(
({ user }) => <div>{user.name}</div>,
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
}
);jsx
// Prevent re-render if props unchanged
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
return <div>{data.value}</div>;
});
// Custom comparison
const Component = React.memo(
({ user }) => <div>{user.name}</div>,
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
}
);useMemo
useMemo
jsx
function ProductList({ products, searchTerm }) {
// Only re-filter when dependencies change
const filteredProducts = useMemo(() => {
console.log('Filtering...');
return products.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [products, searchTerm]);
return <div>{filteredProducts.map(renderProduct)}</div>;
}jsx
function ProductList({ products, searchTerm }) {
// Only re-filter when dependencies change
const filteredProducts = useMemo(() => {
console.log('Filtering...');
return products.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [products, searchTerm]);
return <div>{filteredProducts.map(renderProduct)}</div>;
}useCallback
useCallback
jsx
function Parent() {
const [count, setCount] = useState(0);
// Stable function reference
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <MemoizedChild onClick={handleClick} />;
}
const MemoizedChild = React.memo(({ onClick }) => {
console.log('Child rendered');
return <button onClick={onClick}>Click</button>;
});jsx
function Parent() {
const [count, setCount] = useState(0);
// Stable function reference
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <MemoizedChild onClick={handleClick} />;
}
const MemoizedChild = React.memo(({ onClick }) => {
console.log('Child rendered');
return <button onClick={onClick}>Click</button>;
});Code Splitting
代码分割
Route-Based Splitting
基于路由的分割
jsx
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}jsx
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}Component-Based Splitting
基于组件的分割
jsx
function ProductPage() {
const [showReviews, setShowReviews] = useState(false);
const Reviews = lazy(() => import('./Reviews'));
return (
<div>
<ProductInfo />
<button onClick={() => setShowReviews(true)}>
Show Reviews
</button>
{showReviews && (
<Suspense fallback={<Spinner />}>
<Reviews />
</Suspense>
)}
</div>
);
}jsx
function ProductPage() {
const [showReviews, setShowReviews] = useState(false);
const Reviews = lazy(() => import('./Reviews'));
return (
<div>
<ProductInfo />
<button onClick={() => setShowReviews(true)}>
Show Reviews
</button>
{showReviews && (
<Suspense fallback={<Spinner />}>
<Reviews />
</Suspense>
)}
</div>
);
}List Virtualization
列表虚拟化
React Window
React Window
jsx
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
)}
</FixedSizeList>
);
}jsx
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
)}
</FixedSizeList>
);
}Image Optimization
图片优化
Lazy Loading
懒加载
jsx
function LazyImage({ src, alt }) {
const [isLoaded, setIsLoaded] = useState(false);
const [isInView, setIsInView] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsInView(true);
observer.disconnect();
}
},
{ threshold: 0.1 }
);
if (imgRef.current) observer.observe(imgRef.current);
return () => observer.disconnect();
}, []);
return (
<div ref={imgRef}>
{isInView && (
<img
src={src}
alt={alt}
onLoad={() => setIsLoaded(true)}
style={{ opacity: isLoaded ? 1 : 0 }}
/>
)}
</div>
);
}jsx
function LazyImage({ src, alt }) {
const [isLoaded, setIsLoaded] = useState(false);
const [isInView, setIsInView] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsInView(true);
observer.disconnect();
}
},
{ threshold: 0.1 }
);
if (imgRef.current) observer.observe(imgRef.current);
return () => observer.disconnect();
}, []);
return (
<div ref={imgRef}>
{isInView && (
<img
src={src}
alt={alt}
onLoad={() => setIsLoaded(true)}
style={{ opacity: isLoaded ? 1 : 0 }}
/>
)}
</div>
);
}Bundle Optimization
包优化
Tree Shaking
Tree Shaking
jsx
// Good: Named imports
import { Button, Input } from 'components';
// Bad: Import everything
import * as Components from 'components';jsx
// Good: Named imports
import { Button, Input } from 'components';
// Bad: Import everything
import * as Components from 'components';Dynamic Imports
动态导入
jsx
async function exportData() {
const XLSX = await import('xlsx');
// Use XLSX only when needed
}jsx
async function exportData() {
const XLSX = await import('xlsx');
// Use XLSX only when needed
}Bundle Analysis
包分析
bash
undefinedbash
undefinedCreate React App
Create React App
npm install --save-dev source-map-explorer
npm run build
npx source-map-explorer 'build/static/js/*.js'
npm install --save-dev source-map-explorer
npm run build
npx source-map-explorer 'build/static/js/*.js'
Vite
Vite
npm install --save-dev rollup-plugin-visualizer
undefinednpm install --save-dev rollup-plugin-visualizer
undefinedCommon Mistakes
常见错误
jsx
// ❌ Overusing useMemo
const greeting = useMemo(() => `Hello ${name}`, [name]);
// ✅ Just compute
const greeting = `Hello ${name}`;
// ❌ Inline objects in deps
useEffect(() => {
fetchData(filters);
}, [filters]); // New object every render!
// ✅ Memoize object
const filters = useMemo(() => ({ category, sort }), [category, sort]);
// ❌ Creating functions in render
{items.map(item => <Item onClick={() => handle(item.id)} />)}
// ✅ Use useCallback
const handle = useCallback((id) => {...}, []);
{items.map(item => <Item onClick={handle} id={item.id} />)}jsx
// ❌ Overusing useMemo
const greeting = useMemo(() => `Hello ${name}`, [name]);
// ✅ Just compute
const greeting = `Hello ${name}`;
// ❌ Inline objects in deps
useEffect(() => {
fetchData(filters);
}, [filters]); // New object every render!
// ✅ Memoize object
const filters = useMemo(() => ({ category, sort }), [category, sort]);
// ❌ Creating functions in render
{items.map(item => <Item onClick={() => handle(item.id)} />)}
// ✅ Use useCallback
const handle = useCallback((id) => {...}, []);
{items.map(item => <Item onClick={handle} id={item.id} />)}Web Vitals
Web Vitals
Measuring Performance
性能测量
jsx
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
console.log(metric);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);jsx
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
console.log(metric);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);Best Practices
最佳实践
- Profile before optimizing
- Use React.memo for expensive components
- Memoize only when beneficial
- Split code at route boundaries
- Virtualize long lists
- Lazy load images
- Monitor bundle size
- 先分析再优化
- 为开销大的组件使用React.memo
- 仅在有益时使用记忆化
- 在路由边界处分割代码
- 虚拟化长列表
- 懒加载图片
- 监控包体积
When to Optimize
何时进行优化
DO Optimize
应该优化的场景
- Frequent renders
- Large lists (>100 items)
- Expensive calculations
- Heavy component trees
- 频繁渲染的组件
- 长列表(>100条数据)
- 开销大的计算
- 复杂的组件树
DON'T Optimize
无需优化的场景
- Simple components
- Rare renders
- Cheap calculations
- Small lists (<50 items)
- 简单组件
- 很少渲染的组件
- 开销小的计算
- 短列表(<50条数据)
Practice Exercises
实践练习
- Profile app with React DevTools
- Optimize large product list
- Implement code splitting
- Build virtualized table
- Optimize image gallery
- Reduce bundle size
- Measure Web Vitals
- 使用React DevTools分析应用
- 优化大型产品列表
- 实现代码分割
- 构建虚拟化表格
- 优化图片画廊
- 减小包体积
- 测量Web Vitals
Resources
参考资源
Difficulty: Advanced
Estimated Time: 2-3 weeks
Prerequisites: React Hooks, Component Architecture