Appearance
React 性能优化指南
本文档汇总了 React 应用中常见的性能优化技术和最佳实践,包含至少 20 个优化案例及其使用场景。
1. 使用 React.memo 避免不必要的渲染
使用场景:当组件接收的 props 没有变化时,避免重复渲染。
jsx
import React, { memo } from 'react'
const ExpensiveComponent = memo(({ data }) => {
console.log('ExpensiveComponent 渲染')
return <div>{data}</div>
})
export default ExpensiveComponent注意:React.memo 仅做浅比较,对于复杂对象需要自定义比较函数。
2. 使用 useMemo 缓存计算结果
使用场景:避免在每次渲染时重复计算昂贵的操作。
jsx
import React, { useMemo } from 'react'
const Calculator = ({ items }) => {
const total = useMemo(() => {
console.log('计算总和')
return items.reduce((sum, item) => sum + item.value, 0)
}, [items])
return <div>总和: {total}</div>
}3. 使用 useCallback 缓存函数
使用场景:当函数作为 props 传递给子组件时,避免每次渲染都创建新函数。
jsx
import React, { useCallback } from 'react'
const ParentComponent = () => {
const handleClick = useCallback(() => {
console.log('点击事件')
}, [])
return <ChildComponent onClick={handleClick} />
}4. 合理使用 useState 的更新函数
使用场景:当新状态依赖于旧状态时,使用函数形式更新。
jsx
import React, { useState } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
const increment = () => {
setCount(prevCount => prevCount + 1)
}
return <button onClick={increment}>{count}</button>
}5. 避免在渲染过程中创建新对象
使用场景:防止因对象引用变化导致的不必要渲染。
jsx
// 错误做法
const Component = () => {
return <ChildComponent config={{ theme: 'dark' }} />
}
// 正确做法
const config = { theme: 'dark' }
const Component = () => {
return <ChildComponent config={config} />
}6. 使用虚拟列表处理长列表
使用场景:当需要渲染大量数据时,只渲染可视区域内的元素。
jsx
import { FixedSizeList as List } from 'react-window'
const LongList = ({ items }) => {
const Row = ({ index, style }) => <div style={style}>{items[index]}</div>
return (
<List height={600} itemCount={items.length} itemSize={50} width='100%'>
{Row}
</List>
)
}7. 懒加载组件
使用场景:减少初始加载时间,只在需要时加载组件。
jsx
import React, { lazy, Suspense } from 'react'
const LazyComponent = lazy(() => import('./LazyComponent'))
const App = () => {
return (
<Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</Suspense>
)
}8. 图片优化
使用场景:减少图片加载时间,提升用户体验。
jsx
// 使用适当的图片格式和尺寸
<img src="image.webp" alt="描述" loading="lazy" />
// 使用响应式图片
<picture>
<source media="(max-width: 768px)" srcSet="small.jpg" />
<img src="large.jpg" alt="描述" />
</picture>9. 使用 CSS 动画代替 JavaScript 动画
使用场景:减少 JavaScript 执行时间,利用浏览器硬件加速。
css
/* CSS 动画 */
.fade-in {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}10. 合理使用 Context API
使用场景:避免 Context 值频繁变化导致的全局重渲染。
jsx
// 将不频繁变化的值放在单独的 Context 中
const ThemeContext = createContext()
const UserContext = createContext()
// 只在需要时更新 Context
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light')
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
)
}11. 使用 key 属性优化列表渲染
使用场景:帮助 React 识别列表项的变化,减少 DOM 操作。
jsx
// 错误做法
{items.map((item, index) => (
<div key={index}>{item.name}</div>
));
// 正确做法
{items.map(item => (
<div key={item.id}>{item.name}</div>
));12. 避免使用内联函数和对象
使用场景:减少每次渲染时创建的新引用。
jsx
// 错误做法
;<button onClick={() => handleClick(id)}>点击</button>
// 正确做法
const handleItemClick = useCallback(id => {
// 处理逻辑
}, [])
;<button onClick={() => handleItemClick(id)}>点击</button>13. 使用 Web Workers 处理 heavy 计算
使用场景:将耗时计算移到后台线程,避免阻塞主线程。
jsx
// worker.js
self.onmessage = e => {
const result = heavyCalculation(e.data)
self.postMessage(result)
}
// 主线程
const worker = new Worker('worker.js')
worker.postMessage(data)
worker.onmessage = e => {
console.log('计算结果:', e.data)
}14. 优化网络请求
使用场景:减少网络延迟,提升数据加载速度。
jsx
// 使用缓存
const useData = url => {
const [data, setData] = useState(null)
useEffect(() => {
const fetchData = async () => {
const cached = localStorage.getItem(url)
if (cached) {
setData(JSON.parse(cached))
return
}
const response = await fetch(url)
const result = await response.json()
localStorage.setItem(url, JSON.stringify(result))
setData(result)
}
fetchData()
}, [url])
return data
}15. 使用 shouldComponentUpdate 生命周期方法
使用场景:在类组件中手动控制渲染逻辑。
jsx
class CustomComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// 只在特定条件下更新
return nextProps.data !== this.props.data
}
render() {
return <div>{this.props.data}</div>
}
}16. 合理使用 React DevTools Profiler
使用场景:识别应用中的性能瓶颈。
jsx
import React, { Profiler } from 'react'
const onRender = (id, phase, actualDuration) => {
console.log(`${id} 渲染时间: ${actualDuration}ms`)
}
const App = () => {
return (
<Profiler id='App' onRender={onRender}>
<YourComponent />
</Profiler>
)
}17. 减少 DOM 节点数量
使用场景:减少浏览器渲染负担。
jsx
// 优化前
<div>
<span>标题</span>
<div>内容</div>
</div>
// 优化后
<div>
<h3>标题</h3>
<p>内容</p>
</div>18. 使用 CSS-in-JS 库的性能优化
使用场景:减少运行时样式计算。
jsx
// 使用 styled-components 的静态提取
// babel.config.js
module.exports = {
plugins: [
[
'babel-plugin-styled-components',
{
ssr: true,
displayName: false
}
]
]
}19. 服务端渲染 (SSR)
使用场景:提升首屏加载速度和 SEO。
jsx
// Next.js 示例
import { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async () => {
const data = await fetchData();
return {
props: { data }
};
};
const Page = ({ data }) => {
return <div>{data}</div>;
};
export default Page;20. 代码分割
使用场景:减少初始包大小,提升加载速度。
jsx
// 基于路由的代码分割
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))
const App = () => {
return (
<BrowserRouter>
<Suspense fallback={<div>加载中...</div>}>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/about' element={<About />} />
</Routes>
</Suspense>
</BrowserRouter>
)
}21. 使用 React 18 的并发特性
使用场景:提升应用响应速度,改善用户体验。
jsx
import { startTransition } from 'react'
const App = () => {
const [isPending, startTransition] = useTransition()
const [filter, setFilter] = useState('')
const [data, setData] = useState([])
const handleFilterChange = e => {
const value = e.target.value
setFilter(value)
startTransition(() => {
// 耗时操作
const filteredData = filterData(value)
setData(filteredData)
})
}
return (
<div>
<input value={filter} onChange={handleFilterChange} />
{isPending ? <div>加载中...</div> : <DataList data={data} />}
</div>
)
}22. 优化状态管理
使用场景:减少不必要的全局状态更新。
jsx
// 使用 Zustand 等轻量级状态管理库
import create from 'zustand'
const useStore = create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 }))
}))
const Counter = () => {
const { count, increment } = useStore()
return <button onClick={increment}>{count}</button>
}23. 避免过度使用 useEffect
使用场景:减少不必要的副作用执行。
jsx
// 错误做法
useEffect(() => {
fetchData()
}) // 每次渲染都执行
// 正确做法
useEffect(() => {
fetchData()
}, [dependency]) // 只在依赖变化时执行24. 使用 requestAnimationFrame 优化动画
使用场景:确保动画流畅执行。
jsx
const useAnimation = duration => {
const [progress, setProgress] = useState(0)
useEffect(() => {
let startTime
let animationFrameId
const animate = currentTime => {
if (!startTime) startTime = currentTime
const elapsed = currentTime - startTime
const progress = Math.min(elapsed / duration, 1)
setProgress(progress)
if (progress < 1) {
animationFrameId = requestAnimationFrame(animate)
}
}
animationFrameId = requestAnimationFrame(animate)
return () => cancelAnimationFrame(animationFrameId)
}, [duration])
return progress
}25. 预加载关键资源
使用场景:提升用户交互速度。
jsx
// 预加载图片
const PreloadImage = ({ src }) => {
useEffect(() => {
const img = new Image()
img.src = src
}, [src])
return null
}
// 预加载组件
const PreloadComponent = () => {
useEffect(() => {
// 预加载其他页面的组件
import('./HeavyComponent')
}, [])
return null
}总结
React 性能优化是一个持续的过程,需要根据具体应用场景选择合适的优化策略。以上技术可以帮助你构建更高效、更响应的 React 应用。
优化建议
- 测量先行:使用 React DevTools Profiler 识别性能瓶颈
- 渐进优化:从最明显的性能问题开始,逐步优化
- 按需使用:根据实际需求选择合适的优化技术
- 保持代码简洁:避免过度优化,保持代码可读性
- 持续监控:定期检查应用性能,及时发现问题
通过合理应用这些优化技术,你可以显著提升 React 应用的性能和用户体验。