React Native性能优化 #
概述 #
性能优化是移动应用开发的重要环节。本章节介绍 React Native 应用的性能优化技巧。
渲染优化 #
React.memo #
防止不必要的重新渲染:
tsx
import React from 'react';
import {View, Text} from 'react-native';
interface ItemProps {
id: string;
title: string;
onPress: (id: string) => void;
}
const ListItem: React.FC<ItemProps> = React.memo(({id, title, onPress}) => {
console.log('Rendering item:', id);
return (
<TouchableOpacity onPress={() => onPress(id)}>
<Text>{title}</Text>
</TouchableOpacity>
);
}, (prevProps, nextProps) => {
return prevProps.id === nextProps.id && prevProps.title === nextProps.title;
});
export default ListItem;
useMemo #
缓存计算结果:
tsx
import React, {useMemo} from 'react';
const ExpensiveComponent = ({items}) => {
const sortedItems = useMemo(() => {
console.log('Sorting items...');
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
const totalPrice = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
return (
<View>
<Text>Total: ${totalPrice}</Text>
{sortedItems.map(item => (
<Item key={item.id} item={item} />
))}
</View>
);
};
useCallback #
缓存回调函数:
tsx
import React, {useCallback} from 'react';
const ParentComponent = ({items}) => {
const handleItemPress = useCallback((id: string) => {
console.log('Item pressed:', id);
}, []);
const handleItemDelete = useCallback((id: string) => {
setItems(prev => prev.filter(item => item.id !== id));
}, []);
return (
<FlatList
data={items}
renderItem={({item}) => (
<ListItem
item={item}
onPress={handleItemPress}
onDelete={handleItemDelete}
/>
)}
keyExtractor={item => item.id}
/>
);
};
列表优化 #
FlatList 配置 #
tsx
import React from 'react';
import {FlatList, View, Text} from 'react-native';
const OptimizedList = ({data}) => {
const renderItem = useCallback(({item}) => (
<Item item={item} />
), []);
const keyExtractor = useCallback((item) => item.id, []);
const getItemLayout = useCallback((data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
}), []);
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews={true}
updateCellsBatchingPeriod={50}
ListEmptyComponent={<EmptyComponent />}
ListFooterComponent={<FooterComponent />}
ItemSeparatorComponent={ItemSeparator}
/>
);
};
FlatList 配置说明 #
| 属性 | 说明 | 推荐值 |
|---|---|---|
| initialNumToRender | 初始渲染数量 | 10-20 |
| maxToRenderPerBatch | 每批渲染数量 | 10 |
| windowSize | 渲染窗口大小 | 5-10 |
| removeClippedSubviews | 移除不可见视图 | true |
| updateCellsBatchingPeriod | 批量更新间隔 | 50ms |
| getItemLayout | 固定高度优化 | 必须设置 |
虚拟化列表 #
tsx
const VirtualizedListExample = () => {
const getItem = (data, index) => ({
id: data[index].id,
title: data[index].title,
});
const getItemCount = (data) => data.length;
return (
<VirtualizedList
data={data}
getItem={getItem}
getItemCount={getItemCount}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
);
};
图片优化 #
图片缓存 #
tsx
import React from 'react';
import {Image} from 'react-native';
const CachedImage = ({uri, style}) => {
return (
<Image
source={{
uri,
cache: 'only-if-cached',
}}
style={style}
resizeMode="cover"
/>
);
};
图片预加载 #
tsx
import {Image} from 'react-native';
const preloadImages = (urls: string[]) => {
urls.forEach(url => {
Image.prefetch(url);
});
};
// 使用
preloadImages([
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
]);
图片尺寸优化 #
tsx
const OptimizedImage = ({uri, width}) => {
const [height, setHeight] = useState(200);
useEffect(() => {
Image.getSize(uri, (w, h) => {
const ratio = width / w;
setHeight(h * ratio);
});
}, [uri, width]);
return (
<Image
source={{uri}}
style={{width, height}}
resizeMode="cover"
/>
);
};
内存优化 #
清理副作用 #
tsx
import React, {useEffect, useState} from 'react';
const DataFetcher = () => {
const [data, setData] = useState([]);
useEffect(() => {
let isMounted = true;
let timer: NodeJS.Timeout;
const fetchData = async () => {
const result = await api.getData();
if (isMounted) {
setData(result);
}
};
fetchData();
timer = setInterval(() => {
fetchData();
}, 30000);
return () => {
isMounted = false;
clearInterval(timer);
};
}, []);
return <DataList data={data} />;
};
避免内存泄漏 #
tsx
const useAbortableFetch = (url: string) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url, {
signal: controller.signal,
});
const json = await response.json();
setData(json);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err);
}
} finally {
setLoading(false);
}
};
fetchData();
return () => {
controller.abort();
};
}, [url]);
return {data, loading, error};
};
JavaScript 线程优化 #
InteractionManager #
在交互完成后执行任务:
tsx
import {InteractionManager} from 'react-native';
const HeavyComponent = () => {
const [data, setData] = useState(null);
useEffect(() => {
const task = InteractionManager.runAfterInteractions(() => {
const result = heavyComputation();
setData(result);
});
return () => task.cancel();
}, []);
return <View>{data && <DataDisplay data={data} />}</View>;
};
延迟加载 #
tsx
const LazyComponent = React.lazy(() => import('./HeavyComponent'));
const App = () => {
const [showHeavy, setShowHeavy] = useState(false);
return (
<View>
<Button title="Load Heavy" onPress={() => setShowHeavy(true)} />
{showHeavy && (
<React.Suspense fallback={<Loading />}>
<LazyComponent />
</React.Suspense>
)}
</View>
);
};
动画优化 #
使用原生动画驱动 #
tsx
import Animated, {useAnimatedStyle, useSharedValue, withSpring} from 'react-native-reanimated';
const OptimizedAnimation = () => {
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [{scale: scale.value}],
};
});
const handlePress = () => {
scale.value = withSpring(0.9);
};
return (
<Pressable onPress={handlePress}>
<Animated.View style={[styles.box, animatedStyle]} />
</Pressable>
);
};
避免在动画中使用 JS #
tsx
// 不好 - 使用 JS 驱动
Animated.timing(this.state.opacity, {
toValue: 1,
duration: 500,
useNativeDriver: false, // JS 驱动
}).start();
// 好 - 使用原生驱动
Animated.timing(this.state.opacity, {
toValue: 1,
duration: 500,
useNativeDriver: true, // 原生驱动
}).start();
启动优化 #
延迟初始化 #
tsx
const App = () => {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const init = async () => {
await Promise.all([
initAnalytics(),
initCrashReporting(),
initPushNotifications(),
]);
setIsReady(true);
};
init();
}, []);
if (!isReady) {
return <SplashScreen />;
}
return <MainApp />;
};
代码分割 #
tsx
import {lazy, Suspense} from 'react';
const SettingsScreen = lazy(() => import('./SettingsScreen'));
const ProfileScreen = lazy(() => import('./ProfileScreen'));
const App = () => {
return (
<Suspense fallback={<Loading />}>
<SettingsScreen />
</Suspense>
);
};
性能监控 #
使用 Performance Monitor #
在开发菜单中启用 “Show Perf Monitor”。
自定义性能监控 #
tsx
const PerformanceMonitor = ({children}) => {
useEffect(() => {
const startTime = performance.now();
return () => {
const endTime = performance.now();
console.log(`Component rendered in ${endTime - startTime}ms`);
};
});
return children;
};
使用 React DevTools Profiler #
tsx
import {Profiler} from 'react';
const App = () => {
const onRenderCallback = (
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
) => {
console.log(`${id} ${phase} took ${actualDuration}ms`);
};
return (
<Profiler id="App" onRender={onRenderCallback}>
<MainComponent />
</Profiler>
);
};
最佳实践总结 #
渲染优化 #
- 使用 React.memo 防止不必要渲染
- 使用 useMemo 缓存计算结果
- 使用 useCallback 缓存回调函数
列表优化 #
- 使用 FlatList 而非 ScrollView
- 设置 getItemLayout 提升性能
- 合理配置渲染参数
内存优化 #
- 清理 useEffect 副作用
- 取消未完成的请求
- 避免闭包陷阱
动画优化 #
- 使用原生动画驱动
- 使用 Reanimated 库
- 避免在动画中调用 JS
总结 #
性能优化是持续的过程:
- 渲染优化:减少不必要的渲染
- 列表优化:正确使用 FlatList
- 内存优化:及时清理资源
- 动画优化:使用原生驱动
- 启动优化:延迟初始化
继续学习 动画系统,了解 React Native 动画实现。
最后更新:2026-03-28