Vue 开发者的 React 实战指南:性能优化篇

devtools/2025/1/14 16:56:58/

作为 Vue 开发者,在迁移到 React 开发时,性能优化的思路和方法会有所不同。本文将从 Vue 开发者熟悉的角度出发,详细介绍 React 中的性能优化策略。

渲染优化对比

Vue 的响应式系统

Vue 通过响应式系统自动追踪依赖,只有在数据真正变化时才会触发重渲染

<template><div><h1>{{ title }}</h1><p>{{ description }}</p><!-- 只有 count 变化时才会重渲染 --><div>点击次数:{{ count }}</div><button @click="increment">+1</button></div>
</template><script>
export default {data() {return {title: '标题',description: '描述',count: 0}},methods: {increment() {this.count++}}
}
</script>

React 的渲染机制

React 默认采用自上而下的渲染策略,父组件更新会触发子组件重渲染:

function App() {const [count, setCount] = useState(0);return (<div><h1>标题</h1><p>描述</p>{/* 每次 count 变化,整个组件树都会重新渲染 */}<div>点击次数:{count}</div><button onClick={() => setCount(count + 1)}>+1</button></div>);
}

优化后的版本:

const Title = memo(function Title() {return <h1>标题</h1>;
});const Description = memo(function Description() {return <p>描述</p>;
});const Counter = memo(function Counter({ count, onIncrement }) {return (<><div>点击次数:{count}</div><button onClick={onIncrement}>+1</button></>);
});function App() {const [count, setCount] = useState(0);const increment = useCallback(() => {setCount(c => c + 1);}, []);return (<div><Title /><Description /><Counter count={count} onIncrement={increment} /></div>);
}

组件优化策略

1. 组件拆分与记忆化

// 不好的实践
function ProductList({ products, onSelect }) {return (<div>{products.map(product => (<div key={product.id} onClick={() => onSelect(product)}><img src={product.image} alt={product.name} /><h3>{product.name}</h3><p>{product.price}</p></div>))}</div>);
}// 好的实践
const ProductItem = memo(function ProductItem({ product, onSelect }) {const handleClick = useCallback(() => {onSelect(product);}, [product, onSelect]);return (<div onClick={handleClick}><img src={product.image} alt={product.name} /><h3>{product.name}</h3><p>{product.price}</p></div>);
});function ProductList({ products, onSelect }) {return (<div>{products.map(product => (<ProductItemkey={product.id}product={product}onSelect={onSelect}/>))}</div>);
}

2. 状态管理优化

// 不好的实践
function Dashboard() {const [state, setState] = useState({user: null,products: [],orders: [],settings: {}});// 任何状态更新都会导致整个组件重渲染const updateUser = (user) => {setState(prev => ({ ...prev, user }));};return (<div><UserProfile user={state.user} onUpdate={updateUser} /><ProductList products={state.products} /><OrderList orders={state.orders} /><Settings settings={state.settings} /></div>);
}// 好的实践
function Dashboard() {const [user, setUser] = useState(null);const [products, setProducts] = useState([]);const [orders, setOrders] = useState([]);const [settings, setSettings] = useState({});return (<div><UserProfile user={user} onUpdate={setUser} /><ProductList products={products} /><OrderList orders={orders} /><Settings settings={settings} /></div>);
}

3. 计算属性优化

// 不好的实践
function OrderSummary({ orders }) {// 每次渲染都会重新计算const totalAmount = orders.reduce((sum, order) => sum + order.amount, 0);const completedOrders = orders.filter(order => order.status === 'completed');const pendingOrders = orders.filter(order => order.status === 'pending');return (<div><p>总金额:{totalAmount}</p><p>已完成订单:{completedOrders.length}</p><p>待处理订单:{pendingOrders.length}</p></div>);
}// 好的实践
function OrderSummary({ orders }) {const totalAmount = useMemo(() => {return orders.reduce((sum, order) => sum + order.amount, 0);}, [orders]);const { completedOrders, pendingOrders } = useMemo(() => {return {completedOrders: orders.filter(order => order.status === 'completed'),pendingOrders: orders.filter(order => order.status === 'pending')};}, [orders]);return (<div><p>总金额:{totalAmount}</p><p>已完成订单:{completedOrders.length}</p><p>待处理订单:{pendingOrders.length}</p></div>);
}

列表渲染优化

1. 虚拟列表

function VirtualList({items,itemHeight,windowHeight,overscan = 3
}) {const [scrollTop, setScrollTop] = useState(0);const containerRef = useRef();const visibleCount = Math.ceil(windowHeight / itemHeight);const totalHeight = items.length * itemHeight;const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);const endIndex = Math.min(items.length,Math.ceil((scrollTop + windowHeight) / itemHeight) + overscan);const visibleItems = useMemo(() => {return items.slice(startIndex, endIndex).map((item, index) => ({...item,index: startIndex + index}));}, [items, startIndex, endIndex]);const handleScroll = useCallback((e) => {setScrollTop(e.target.scrollTop);}, []);return (<divref={containerRef}style={{ height: windowHeight, overflow: 'auto' }}onScroll={handleScroll}><div style={{ height: totalHeight, position: 'relative' }}>{visibleItems.map(item => (<divkey={item.id}style={{position: 'absolute',top: item.index * itemHeight,height: itemHeight}}>{item.content}</div>))}</div></div>);
}

2. 无限滚动

function InfiniteList({ fetchItems, itemHeight = 50 }) {const [items, setItems] = useState([]);const [loading, setLoading] = useState(false);const [hasMore, setHasMore] = useState(true);const [page, setPage] = useState(1);const containerRef = useRef();const loadMore = useCallback(async () => {if (loading || !hasMore) return;setLoading(true);try {const newItems = await fetchItems(page);if (newItems.length === 0) {setHasMore(false);} else {setItems(prev => [...prev, ...newItems]);setPage(p => p + 1);}} finally {setLoading(false);}}, [fetchItems, page, loading, hasMore]);useEffect(() => {const container = containerRef.current;if (!container) return;const observer = new IntersectionObserver(entries => {if (entries[0].isIntersecting) {loadMore();}},{ threshold: 0.5 });const sentinel = container.lastElementChild;if (sentinel) {observer.observe(sentinel);}return () => observer.disconnect();}, [loadMore]);return (<div ref={containerRef} style={{ height: '100vh', overflow: 'auto' }}>{items.map(item => (<div key={item.id} style={{ height: itemHeight }}>{item.content}</div>))}{hasMore && (<div style={{ height: itemHeight, textAlign: 'center' }}>{loading ? '加载中...' : '向下滚动加载更多'}</div>)}</div>);
}

数据获取优化

1. 请求缓存

function useQuery(key, fetcher, options = {}) {const cache = useRef(new Map());const [data, setData] = useState(null);const [error, setError] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {const fetchData = async () => {if (cache.current.has(key) && !options.forceRefetch) {setData(cache.current.get(key));setLoading(false);return;}setLoading(true);try {const result = await fetcher();cache.current.set(key, result);setData(result);setError(null);} catch (err) {setError(err);setData(null);} finally {setLoading(false);}};fetchData();}, [key, fetcher, options.forceRefetch]);return { data, error, loading };
}

2. 请求去重

function useDedupeQuery(key, fetcher) {const pendingRequests = useRef(new Map());const executeQuery = useCallback(async () => {if (pendingRequests.current.has(key)) {return pendingRequests.current.get(key);}const promise = fetcher();pendingRequests.current.set(key, promise);try {const result = await promise;pendingRequests.current.delete(key);return result;} catch (error) {pendingRequests.current.delete(key);throw error;}}, [key, fetcher]);return useQuery(key, executeQuery);
}

代码分割

1. 路由级别分割

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));function App() {return (<Suspense fallback={<Loading />}><Routes><Route path="/" element={<Dashboard />} /><Route path="/profile" element={<Profile />} /><Route path="/settings" element={<Settings />} /></Routes></Suspense>);
}

2. 组件级别分割

const HeavyChart = lazy(() => import('./components/HeavyChart'));function Dashboard() {const [showChart, setShowChart] = useState(false);return (<div><button onClick={() => setShowChart(true)}>显示图表</button>{showChart && (<Suspense fallback={<Loading />}><HeavyChart /></Suspense>)}</div>);
}

工具和监控

1. 性能分析

import { Profiler } from 'react';function onRenderCallback(id,phase,actualDuration,baseDuration,startTime,commitTime,interactions
) {console.log({id,phase,actualDuration,baseDuration,startTime,commitTime,interactions});
}function App() {return (<Profiler id="App" onRender={onRenderCallback}><div>{/* 应用内容 */}</div></Profiler>);
}

2. 性能监控

function usePerformanceMonitor() {useEffect(() => {const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {if (entry.entryType === 'largest-contentful-paint') {console.log('LCP:', entry.startTime);}if (entry.entryType === 'first-input') {console.log('FID:', entry.processingStart - entry.startTime);}if (entry.entryType === 'layout-shift') {console.log('CLS:', entry.value);}}});observer.observe({entryTypes: ['largest-contentful-paint', 'first-input', 'layout-shift']});return () => observer.disconnect();}, []);
}

最佳实践

  1. 渲染优化

    • 合理拆分组件
    • 使用 memo 避免不必要的重渲染
    • 优化计算属性
    • 合理使用 Context
  2. 状态管理

    • 状态粒度适中
    • 避免冗余状态
    • 使用不可变数据
    • 合理使用状态管理库
  3. 数据处理

    • 实现请求缓存
    • 避免重复请求
    • 优化大数据渲染
    • 使用虚拟列表
  4. 代码组织

    • 合理代码分割
    • 按需加载
    • 预加载关键资源
    • 优化打包体积

小结

  1. React 性能优化的特点:

    • 组件级别优化
    • 状态管理优化
    • 渲染机制优化
    • 资源加载优化
  2. 从 Vue 到 React 的转变:

    • 理解渲染机制差异
    • 掌握优化工具
    • 建立性能意识
    • 实践优化策略
  3. 开发建议:

    • 先测量后优化
    • 避免过早优化
    • 关注用户体验
    • 持续监控改进

下一篇文章,我们将深入探讨 React 的测试策略,帮助你构建可靠的应用。

如果觉得这篇文章对你有帮助,别忘了点个赞 👍


http://www.ppmy.cn/devtools/150457.html

相关文章

Java(五十)java-IO流-缓冲流(BufferedInputStream和BufferedOutputStream)

接下来我们学习一下java缓冲流中的读取和写入类BufferedInputStream&#xff08;缓冲字节输入流&#xff09;和BufferedOutputStream&#xff08;缓冲字节输出流&#xff09;类&#xff0c;这个两个类的使用方法和IO流中的FileOutputStream和FileInputStream类是差不多的。但是…

hive3后创建表默认是外部表问题

hive3 hive4创建的表总是外部表&#xff0c;但是操作使用和内部表一样&#xff0c;修改成为默认是内部表 <property><name>metastore.metadata.transformer.class</name><value> </value> </property>注意空格是必须的 后面执行 desc fo…

从epoll事件的视角探讨TCP:三次握手、四次挥手、应用层与传输层之间的联系

目录 一、应用层与TCP之间的联系 二、 当通信双方中的一方如客户端主动断开连接时&#xff0c;仅是在客户端的视角下连接已经断开&#xff0c;在服务端的眼中&#xff0c;连接依然存在&#xff0c;为什么&#xff1f;——触发EPOLLRDHUP事件&#xff1a;对端关闭连接或停止写…

LabVIEW驱动电机实现样品自动搜索

利用LabVIEW控制电机驱动相机在XY平面上进行扫描&#xff0c;以检测样品位置。样品最初可能位于相机视野范围之外&#xff0c;需要实现自动搜索样品位置并完成精确定位扫描的功能。该系统需具有以下特点&#xff1a; 高效搜索&#xff1a;能够快速确定样品位置&#xff0c;缩短…

电脑之故障检测(Computer Fault Detection)

电脑之故障检测 在日常使用电脑的过程中&#xff0c;我们难免会遇到各种各样的故障。从简单的软件冲突到复杂的硬件损坏&#xff0c;这些问题往往让人头疼不已。然而&#xff0c;掌握一些基本的电脑故障检测方法&#xff0c;可以帮助我们快速定位问题所在&#xff0c;并采取相…

CHAIN OF RESPONSIBILITY(职责链)—对象行为型模式

1. 意图 使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链&#xff0c;并沿着这条链传递该请求&#xff0c;直到有一个对象处理它为止。 2. 动机 考虑一个图形用户界面中的上下文有关的帮助机制。用户在界面的任一部分…

1.组件的三大组成部分注意点(结构/样式/逻辑)scoped解决样式冲突/data是一个函数2.组件通信组件通信语法父传子子传父

学习目标 1.组件的三大组成部分注意点&#xff08;结构/样式/逻辑&#xff09; scoped解决样式冲突/data是一个函数 2.组件通信 组件通信语法 父传子 子传父 非父子通信&#xff08;扩展&#xff09; 3.综合案例&#xff1a;小黑记事本&#xff08;组件版&#xff09; …

两分钟解决 :![rejected] master -> master (fetch first) , 无法正常push到远端库

目录 分析问题的原因解决 分析问题的原因 在git push的时候莫名遇到这种情况 若你在git上修改了如README.md的文件。由于本地是没有README.md文件的&#xff0c;所以导致 远端仓库git和本地不同步。 将远端、本地进行合并就可以很好的解决这个问题 注意&#xff1a;直接git pu…