Skip to content

性能优化

1. 性能优化简介

在 React Native 应用开发中,性能优化是一个非常重要的环节。良好的性能可以提升用户体验,减少应用崩溃的可能性,延长电池寿命。性能优化主要包括以下几个方面:

  • 列表优化:优化长列表的渲染性能
  • 图片优化:减少图片加载时间和内存占用
  • 内存管理:避免内存泄漏和过度使用内存
  • 渲染优化:减少不必要的渲染
  • 网络优化:优化网络请求和数据处理

2. 列表优化

2.1 使用 FlatList 而非 ScrollView

对于长列表,应该使用 FlatList 而不是 ScrollView,因为 FlatList 实现了虚拟化,只渲染可见区域的项目。

javascript
// 不推荐:使用 ScrollView 渲染长列表
<ScrollView>
  {items.map(item => (
    <Item key={item.id} item={item} />
  ))}
</ScrollView>

// 推荐:使用 FlatList 渲染长列表
<FlatList
  data={items}
  keyExtractor={item => item.id}
  renderItem={({ item }) => <Item item={item} />}
/>

2.2 优化 FlatList

2.2.1 设置适当的窗口大小

javascript
<FlatList
  data={items}
  keyExtractor={item => item.id}
  renderItem={({ item }) => <Item item={item} />}
  windowSize={10} // 控制预加载的项目数量
  maxToRenderPerBatch={5} // 每批渲染的项目数量
  updateCellsBatchingPeriod={100} // 批处理更新的时间间隔
  removeClippedSubviews={true} // 移除不可见的子视图
/>

2.2.2 使用 getItemLayout

如果列表项的高度是固定的,使用 getItemLayout 可以避免测量每个项目的高度,提高性能。

javascript
<FlatList
  data={items}
  keyExtractor={item => item.id}
  renderItem={({ item }) => <Item item={item} />}
  getItemLayout={(data, index) => ({
    length: 100, // 每个项目的高度
    offset: 100 * index,
    index,
  })}
/>

2.2.3 使用 initialNumToRender

设置 initialNumToRender 可以控制初始渲染的项目数量,减少初始加载时间。

javascript
<FlatList
  data={items}
  keyExtractor={item => item.id}
  renderItem={({ item }) => <Item item={item} />}
  initialNumToRender={10} // 初始渲染的项目数量
/>

2.2.4 使用 shouldComponentUpdate 或 React.memo

对于列表项组件,使用 shouldComponentUpdateReact.memo 可以避免不必要的重新渲染。

javascript
// 使用 React.memo
const Item = React.memo(({ item }) => {
  return (
    <View style={styles.item}>
      <Text>{item.title}</Text>
    </View>
  );
});

// 或者使用 shouldComponentUpdate
class Item extends React.Component {
  shouldComponentUpdate(nextProps) {
    return this.props.item.id !== nextProps.item.id ||
           this.props.item.title !== nextProps.item.title;
  }
  
  render() {
    const { item } = this.props;
    return (
      <View style={styles.item}>
        <Text>{item.title}</Text>
      </View>
    );
  }
}

2.3 虚拟列表

对于非常长的列表,可以使用虚拟列表库,如 react-windowreact-virtualized

bash
npm install react-window
javascript
import { FixedSizeList as List } from 'react-window';

const VirtualList = ({ items }) => {
  const Row = ({ index, style }) => (
    <div style={style}>
      <Item item={items[index]} />
    </div>
  );

  return (
    <List
      height={600}
      itemCount={items.length}
      itemSize={100}
      width="100%"
    >
      {Row}
    </List>
  );
};

3. 图片优化

3.1 图片格式选择

  • PNG:适合图标和图形,支持透明
  • JPG:适合照片,压缩率高
  • WebP:Google 开发的格式,压缩率更高,支持透明

3.2 图片尺寸优化

  • 使用适当尺寸的图片,避免使用过大的图片
  • 为不同设备提供不同分辨率的图片
  • 使用 resizeMode 属性控制图片显示方式
javascript
<Image
  source={require('./image.png')}
  style={{ width: 100, height: 100 }}
  resizeMode="cover" // 'cover', 'contain', 'stretch', 'repeat', 'center'
/>

3.3 图片加载优化

3.3.1 使用 Image.getSize

在加载图片前获取图片尺寸,避免布局跳动。

javascript
Image.getSize(
  'https://example.com/image.jpg',
  (width, height) => {
    console.log('图片尺寸:', width, height);
    // 计算合适的显示尺寸
  },
  error => {
    console.error('获取图片尺寸失败:', error);
  }
);

3.3.2 使用占位符

在图片加载完成前显示占位符,提升用户体验。

javascript
const [loaded, setLoaded] = useState(false);

<Image
  source={{ uri: 'https://example.com/image.jpg' }}
  style={{ width: 100, height: 100 }}
  onLoad={() => setLoaded(true)}
>
  {!loaded && (
    <View style={{ width: 100, height: 100, backgroundColor: '#f0f0f0' }}>
      <ActivityIndicator />
    </View>
  )}
</Image>

3.3.3 图片缓存

使用图片缓存库,如 react-native-fast-image,提高图片加载速度。

bash
npm install react-native-fast-image
javascript
import FastImage from 'react-native-fast-image';

<FastImage
  source={{
    uri: 'https://example.com/image.jpg',
    priority: FastImage.priority.high,
  }}
  style={{ width: 100, height: 100 }}
  resizeMode={FastImage.resizeMode.cover}
/>

3.4 懒加载

对于长列表中的图片,使用懒加载可以减少初始加载时间和内存使用。

javascript
import { FlatList } from 'react-native';
import FastImage from 'react-native-fast-image';

const LazyImage = ({ uri, style }) => {
  const [isVisible, setIsVisible] = useState(false);
  
  return (
    <View style={style}>
      {isVisible ? (
        <FastImage
          source={{ uri }}
          style={style}
          resizeMode={FastImage.resizeMode.cover}
        />
      ) : (
        <View style={[style, { backgroundColor: '#f0f0f0' }]} />
      )}
      <View
        onViewableItemsChanged={({ viewableItems }) => {
          if (viewableItems.length > 0) {
            setIsVisible(true);
          }
        }}
        viewabilityConfig={{
          itemVisiblePercentThreshold: 50,
        }}
      />
    </View>
  );
};

<FlatList
  data={items}
  keyExtractor={item => item.id}
  renderItem={({ item }) => (
    <LazyImage uri={item.image} style={{ width: 100, height: 100 }} />
  )}
/>

4. 渲染优化

4.1 使用 React.memo

对于纯展示组件,使用 React.memo 可以避免不必要的重新渲染。

javascript
const MyComponent = React.memo(({ prop1, prop2 }) => {
  return (
    <View>
      <Text>{prop1}</Text>
      <Text>{prop2}</Text>
    </View>
  );
});

4.2 使用 useCallback 和 useMemo

使用 useCallback 可以缓存函数,使用 useMemo 可以缓存计算结果。

javascript
const MyComponent = ({ items, onItemPress }) => {
  // 缓存函数
  const handlePress = useCallback((id) => {
    onItemPress(id);
  }, [onItemPress]);
  
  // 缓存计算结果
  const total = useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);
  
  return (
    <View>
      <Text>Total: {total}</Text>
      {items.map(item => (
        <TouchableOpacity key={item.id} onPress={() => handlePress(item.id)}>
          <Text>{item.name}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
};

4.3 避免在渲染过程中创建函数

在渲染过程中创建函数会导致每次渲染都创建新的函数实例,影响性能。

javascript
// 不推荐
const MyComponent = ({ items }) => {
  return (
    <View>
      {items.map(item => (
        <TouchableOpacity
          key={item.id}
          onPress={() => console.log('Pressed:', item.id)} // 每次渲染都会创建新函数
        >
          <Text>{item.name}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
};

// 推荐
const MyComponent = ({ items }) => {
  const handlePress = useCallback((id) => {
    console.log('Pressed:', id);
  }, []);
  
  return (
    <View>
      {items.map(item => (
        <TouchableOpacity
          key={item.id}
          onPress={() => handlePress(item.id)}
        >
          <Text>{item.name}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
};

4.4 避免在渲染过程中进行计算

在渲染过程中进行计算会导致每次渲染都重新计算,影响性能。

javascript
// 不推荐
const MyComponent = ({ items }) => {
  const total = items.reduce((sum, item) => sum + item.value, 0); // 每次渲染都会重新计算
  
  return (
    <View>
      <Text>Total: {total}</Text>
    </View>
  );
};

// 推荐
const MyComponent = ({ items }) => {
  const total = useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);
  
  return (
    <View>
      <Text>Total: {total}</Text>
    </View>
  );
};

5. 内存管理

5.1 避免内存泄漏

  • 清理定时器和监听器
  • 清理网络请求
  • 清理事件监听器
javascript
const MyComponent = () => {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('Timer tick');
    }, 1000);
    
    const subscription = someEventEmitter.addListener('event', handleEvent);
    
    return () => {
      clearInterval(timer); // 清理定时器
      subscription.remove(); // 清理监听器
    };
  }, []);
  
  return <View />;
};

5.2 优化状态管理

  • 避免在状态中存储过大的数据
  • 使用适当的状态管理库(如 Redux Toolkit)
  • 合理设计状态结构

5.3 图片内存管理

  • 及时释放不再需要的图片资源
  • 使用适当的图片尺寸
  • 避免在内存中缓存过多图片

6. 网络优化

6.1 批量请求

  • 合并多个网络请求
  • 使用 GraphQL 减少过度获取
  • 实现请求缓存

6.2 延迟加载

  • 实现数据的分页加载
  • 按需加载数据
  • 使用骨架屏提升用户体验

6.3 网络状态检测

  • 检测网络状态变化
  • 在网络不可用时提供离线体验
  • 实现数据同步机制
javascript
import NetInfo from '@react-native-community/netinfo';

const MyComponent = () => {
  const [isConnected, setIsConnected] = useState(true);
  
  useEffect(() => {
    const unsubscribe = NetInfo.addEventListener(state => {
      setIsConnected(state.isConnected);
    });
    
    return unsubscribe;
  }, []);
  
  return (
    <View>
      {isConnected ? (
        <Text>在线</Text>
      ) : (
        <Text>离线</Text>
      )}
    </View>
  );
};

7. 最佳实践

  1. 使用适当的组件:根据场景选择合适的组件,如使用 FlatList 渲染长列表
  2. 优化渲染:使用 React.memo、useCallback、useMemo 等优化渲染性能
  3. 图片优化:选择合适的图片格式和尺寸,使用缓存和懒加载
  4. 内存管理:避免内存泄漏,及时清理资源
  5. 网络优化:批量请求,延迟加载,检测网络状态
  6. 代码分割:使用动态导入减少初始包大小
  7. 性能监控:使用 Flipper 和 Chrome DevTools 监控性能
  8. 定期测试:在不同设备上测试应用性能

8. 常见性能问题与解决方案

8.1 列表滚动卡顿

问题:长列表滚动时卡顿

解决方案

  • 使用 FlatList 而非 ScrollView
  • 优化 FlatList 的属性
  • 使用 React.memo 优化列表项
  • 减少列表项的复杂度

8.2 图片加载慢

问题:图片加载时间长

解决方案

  • 使用适当尺寸的图片
  • 使用 WebP 格式
  • 使用图片缓存库
  • 实现图片懒加载

8.3 应用启动慢

问题:应用启动时间长

解决方案

  • 减少初始包大小
  • 使用代码分割
  • 延迟加载非关键资源
  • 优化初始渲染

8.4 内存占用过高

问题:应用内存占用过高

解决方案

  • 清理定时器和监听器
  • 优化图片加载
  • 合理管理状态
  • 使用内存分析工具

9. 扩展阅读

10. 完整示例

10.1 优化后的列表

javascript
// OptimizedList.js
import React, { useCallback, useMemo } from 'react';
import { View, Text, FlatList, StyleSheet, TouchableOpacity } from 'react-native';

const Item = React.memo(({ item, onPress }) => {
  return (
    <TouchableOpacity style={styles.item} onPress={() => onPress(item.id)}>
      <Text style={styles.title}>{item.title}</Text>
      <Text style={styles.description}>{item.description}</Text>
    </TouchableOpacity>
  );
});

const OptimizedList = ({ items, onItemPress }) => {
  const handlePress = useCallback((id) => {
    onItemPress(id);
  }, [onItemPress]);

  const keyExtractor = useCallback((item) => item.id, []);

  const getItemLayout = useCallback((data, index) => ({
    length: 100,
    offset: 100 * index,
    index,
  }), []);

  const renderItem = useCallback(({ item }) => (
    <Item item={item} onPress={handlePress} />
  ), [handlePress]);

  return (
    <FlatList
      data={items}
      keyExtractor={keyExtractor}
      renderItem={renderItem}
      getItemLayout={getItemLayout}
      windowSize={10}
      maxToRenderPerBatch={5}
      updateCellsBatchingPeriod={100}
      removeClippedSubviews={true}
      initialNumToRender={10}
      style={styles.container}
    />
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 10,
    marginVertical: 5,
    marginHorizontal: 10,
    height: 100,
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
  },
  description: {
    fontSize: 14,
    color: '#666',
  },
});

export default OptimizedList;

10.2 优化后的图片加载

javascript
// OptimizedImage.js
import React, { useState } from 'react';
import { View, ActivityIndicator, StyleSheet } from 'react-native';
import FastImage from 'react-native-fast-image';

const OptimizedImage = ({ uri, style, placeholderColor = '#f0f0f0' }) => {
  const [isLoading, setIsLoading] = useState(true);

  return (
    <View style={[style, isLoading && { backgroundColor: placeholderColor }]}>
      <FastImage
        source={{ uri }}
        style={style}
        resizeMode={FastImage.resizeMode.cover}
        onLoadStart={() => setIsLoading(true)}
        onLoadEnd={() => setIsLoading(false)}
      />
      {isLoading && (
        <View style={[StyleSheet.absoluteFillObject, styles.loadingContainer]}>
          <ActivityIndicator size="small" color="#999" />
        </View>
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  loadingContainer: {
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(255, 255, 255, 0.8)',
  },
});

export default OptimizedImage;

10.3 性能监控示例

javascript
// PerformanceMonitor.js
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import { usePerformanceMonitor } from '@react-native-firebase/perf';

const PerformanceMonitor = () => {
  const perf = usePerformanceMonitor();
  const [traceResults, setTraceResults] = useState([]);

  const runPerformanceTest = async () => {
    // 创建性能跟踪
    const trace = perf.newTrace('heavy-operation');
    
    try {
      trace.start();
      
      // 执行耗时操作
      await new Promise(resolve => {
        let result = 0;
        for (let i = 0; i < 10000000; i++) {
          result += i;
        }
        setTimeout(resolve, 100);
      });
      
      trace.stop();
      
      // 获取跟踪结果
      const metrics = await trace.getMetrics();
      setTraceResults(prev => [...prev, metrics]);
    } catch (error) {
      console.error('性能测试失败:', error);
      trace.stop();
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>性能监控示例</Text>
      <Button title="运行性能测试" onPress={runPerformanceTest} />
      
      <View style={styles.results}>
        {traceResults.map((result, index) => (
          <View key={index} style={styles.resultItem}>
            <Text>测试 #{index + 1}</Text>
            <Text>执行时间: {result.duration ? result.duration.toFixed(2) : 'N/A'} ms</Text>
          </View>
        ))}
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  results: {
    marginTop: 20,
  },
  resultItem: {
    backgroundColor: '#f0f0f0',
    padding: 10,
    marginBottom: 10,
    borderRadius: 5,
  },
});

export default PerformanceMonitor;

这些示例展示了如何优化列表渲染、图片加载和监控应用性能,帮助开发者构建更流畅、更响应的 React Native 应用。

© 2026 编程马·菜鸟教程 版权所有