Skip to content

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 应用。

优化建议

  1. 测量先行:使用 React DevTools Profiler 识别性能瓶颈
  2. 渐进优化:从最明显的性能问题开始,逐步优化
  3. 按需使用:根据实际需求选择合适的优化技术
  4. 保持代码简洁:避免过度优化,保持代码可读性
  5. 持续监控:定期检查应用性能,及时发现问题

通过合理应用这些优化技术,你可以显著提升 React 应用的性能和用户体验。

基于 VitePress 的本地知识库