欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
📌 开发环境声明:本文基于 React Native 0.72.90 版本进行开发适配
🚀 一、开篇引言
在移动应用开发中,图片加载是最常见也是最容易出现性能问题的地方。React Native 原生的 Image 组件虽然能满足基本需求,但在处理大量图片列表、内存缓存、磁盘缓存等方面存在明显短板。
react-native-fast-image正是为解决这些痛点而生的高性能图片加载组件,它底层使用 Glide(Android)、SDWebImage(iOS)以及鸿蒙原生图片加载能力,提供了完善的缓存机制和优先级加载功能。本文将带你深入了解如何在 HarmonyOS 平台上集成和使用这个强大的图片加载库。
1.1 图片加载的痛点与挑战
在实际开发中,我们经常会遇到以下问题:
| 问题场景 | 原生 Image 表现 | FastImage 表现 |
|---|---|---|
| 长列表图片滚动 | 卡顿明显,内存飙升 | 流畅滑动,内存稳定 |
| 图片重复加载 | 每次都重新请求 | 自动缓存,秒开 |
| 内存管理 | 手动管理,易泄漏 | 自动清理,智能回收 |
| 加载优先级 | 无法控制 | 支持优先级队列 |
| GIF 动图 | 支持有限 | 完美支持 |
📦 二、库概览
2.1 基本信息
| 项目 | 内容 |
|---|---|
| 库名称 | react-native-fast-image |
| 鸿蒙包名 | @react-native-ohos/react-native-fast-image |
| 官方仓库 | https://github.com/DylanVann/react-native-fast-image |
| 鸿蒙仓库 | https://atomgit.com/openharmony-sig/rntpc_react-native-fast-image |
| 开源协议 | MIT |
2.2 版本兼容性
| 三方库版本 | RN 版本 | 发布地址 |
|---|---|---|
| 8.6.4 | 0.72 | @react-native-ohos/react-native-fast-image |
| 8.7.0 | 0.77 | @react-native-ohos/react-native-fast-image |
2.3 核心优势对比
┌─────────────────────────────────────────────────────────────────┐
│ 功能对比 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 原生 Image FastImage │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ✓ 基础加载 │ → │ ✓ 基础加载 │ │
│ │ ✓ 样式设置 │ │ ✓ 样式设置 │ │
│ │ │ │ ✓ 内存缓存 │ │
│ │ │ │ ✓ 磁盘缓存 │ │
│ │ │ │ ✓ 优先级加载 │ │
│ │ │ │ ✓ 预加载 │ │
│ │ │ │ ✓ 缓存清理 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 功能子集 ────────────────────► 功能超集 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.4 适用场景分析
| 场景 | 推荐指数 | 说明 |
|---|---|---|
| 图片列表/瀑布流 | ⭐⭐⭐⭐⭐ | 缓存机制大幅提升性能 |
| 头像加载 | ⭐⭐⭐⭐⭐ | 自动缓存,避免重复请求 |
| 轮播图/引导页 | ⭐⭐⭐⭐⭐ | 支持预加载,体验流畅 |
| GIF 动图展示 | ⭐⭐⭐⭐⭐ | 完美支持动图播放 |
| 单张静态图片 | ⭐⭐⭐ | 可用原生 Image 替代 |
🔧 三、安装与配置
3.1 安装依赖(这里使用oh-tpl下的)
在项目根目录执行以下命令:
bash
npm install @react-native-oh-tpl/react-native-fast-image@8.6.3-0.4.17-rc.1
或使用 yarn:
bash
yarn add @react-native-ohos/react-native-fast-image
3.2 配置网络权限
由于 FastImage 需要加载网络图片,必须在 HarmonyOS 模块配置文件中声明网络权限。
打开 harmony/entry/src/main/module.json5,在 requestPermissions 数组中添加网络权限:
json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
⚠️ 注意:如果不配置网络权限,加载网络图片时会静默失败,不会显示任何错误提示。
3.3 HarmonyOS 原生端配置
步骤一:配置 oh-package.json5
打开 harmony/oh-package.json5,添加 overrides 字段:
json
{
"overrides": {
"@rnoh/react-native-openharmony": "./react_native_openharmony"
}
}
步骤二:引入 har 包
打开 harmony/entry/oh-package.json5,添加依赖:
json
"dependencies": {
"@rnoh/react-native-openharmony": "file:../react_native_openharmony",
"@react-native-ohos/react-native-fast-image": "file:../../node_modules/@react-native-ohos/react-native-fast-image/harmony/fast_image.har"
}
步骤三:配置 CMakeLists.txt
打开 harmony/entry/src/main/cpp/CMakeLists.txt,添加以下内容:
diff
project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
# RNOH_BEGIN: manual_package_linking_1
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-fast-image/src/main/cpp" ./fast-image)
# RNOH_END: manual_package_linking_1
add_library(rnoh_app SHARED
${GENERATED_CPP_FILES}
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
# RNOH_BEGIN: manual_package_linking_2
+ target_link_libraries(rnoh_app PUBLIC rnoh_fast_image)
# RNOH_END: manual_package_linking_2
步骤四:配置 PackageProvider.cpp
打开 harmony/entry/src/main/cpp/PackageProvider.cpp,添加:
diff
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "FastImagePackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
+ std::make_shared<FastImagePackage>(ctx),
};
}
步骤五:配置 RNPackagesFactory.ts
打开 harmony/entry/src/main/ets/RNPackagesFactory.ts,添加:
diff
+ import { FastImagePackage } from '@react-native-ohos/react-native-fast-image/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
+ new FastImagePackage(ctx)
];
}
步骤六:同步依赖
在 DevEco Studio 中点击右上角的 sync 按钮,或在终端执行:
bash
cd harmony/entry
ohpm install
3.4 验证安装
安装完成后,检查 package.json 中是否包含依赖:
json
{
"dependencies": {
"@react-native-ohos/react-native-fast-image": "^8.6.4"
}
}
🎯 四、核心属性详解
4.1 图片源配置(source)
FastImage 支持多种图片源配置方式:
远程图片:
tsx
<FastImage
source={{
uri: 'https://example.com/image.jpg',
headers: { Authorization: 'Bearer token' },
}}
/>
本地图片:
tsx
<FastImage
source={require('./local-image.png')}
/>
| 属性 | 类型 | 说明 |
|---|---|---|
| uri | string | 远程图片地址 |
| headers | object | 请求头(用于鉴权) |
| priority | 'low'| 'normal' | 'high' | 加载优先级(鸿蒙暂不支持) |
| cache | 'immutable'| 'web' | 'cacheOnly' | 缓存策略(鸿蒙暂不支持) |
4.2 缩放模式(resizeMode)
FastImage 提供四种缩放模式,效果对比:
┌─────────────────────────────────────────────────────────────┐
│ │
│ contain cover stretch center │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ ████ │ │████████│ │████████│ │ │ │
│ │ ████ │ │████████│ │████████│ │ ████ │ │
│ │ │ │████████│ │████████│ │ ████ │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ │
│ 保持比例 填充容器 拉伸填满 原始大小 │
│ 可能留白 可能裁剪 可能变形 居中显示 │
│ │
└─────────────────────────────────────────────────────────────┘
tsx
<FastImage
source={{ uri: 'https://example.com/image.jpg' }}
resizeMode={FastImage.resizeMode.cover}
/>
4.3 加载状态回调
FastImage 提供完整的加载生命周期回调:
Cache Network FastImage App Cache Network FastImage App alt [缓存命中] [缓存未命中] 设置 source 检查缓存 返回图片 onLoad onLoadStart 请求图片 数据传输 onProgress 加载完成 写入缓存 onLoad onLoadEnd
| 回调 | 参数 | 说明 |
|---|---|---|
| onLoadStart | - | 开始加载 |
| onProgress | { nativeEvent: { loaded, total } } | 加载进度 |
| onLoad | { nativeEvent: { width, height } } | 加载成功 |
| onError | - | 加载失败 |
| onLoadEnd | - | 加载结束(成功或失败) |
4.4 样式属性
| 属性 | 类型 | 说明 |
|---|---|---|
| style | ImageStyle | 容器样式 |
| tintColor | string| number | 图片着色(用于图标) |
| defaultSource | number | 加载占位图 |
tintColor 着色效果:
tsx
<FastImage
source={require('./icon.png')}
tintColor="#007AFF"
style={{ width: 24, height: 24 }}
/>
⚡ 五、静态方法详解
5.1 图片预加载(preload)
预加载可以在用户访问前提前下载图片,提升用户体验:
tsx
FastImage.preload([
{ uri: 'https://example.com/image1.jpg' },
{ uri: 'https://example.com/image2.jpg' },
{ uri: 'https://example.com/image3.jpg' },
]);
适用场景:
- 引导页图片预加载
- 轮播图预加载
- 详情页图片预加载
5.2 缓存清理
FastImage 提供两种缓存清理方式:
tsx
// 清除内存缓存(快速,适合切换页面时调用)
await FastImage.clearMemoryCache();
// 清除磁盘缓存(较慢,适合设置页面调用)
await FastImage.clearDiskCache();
最佳实践:
tsx
// 用户退出登录时清理缓存
const handleLogout = async () => {
await FastImage.clearMemoryCache();
await FastImage.clearDiskCache();
// ... 其他清理逻辑
};
💡 六、完整实战示例
下面是一个完整的高性能图片列表示例,包含加载进度、错误处理、预加载等功能:

ts
import React, { useEffect, useState, useCallback } from 'react';
import {
View,
Text,
FlatList,
StyleSheet,
RefreshControl,
ActivityIndicator,
TouchableOpacity,
} from 'react-native';
import FastImage, { OnLoadEvent, OnProgressEvent } from 'react-native-fast-image';
interface ImageItem {
id: string;
url: string;
title: string;
}
const IMAGE_LIST: ImageItem[] = [
{ id: '1', url: 'https://picsum.photos/400/300?random=1', title: '风景图片一' },
{ id: '2', url: 'https://picsum.photos/400/300?random=2', title: '风景图片二' },
{ id: '3', url: 'https://picsum.photos/400/300?random=3', title: '风景图片三' },
{ id: '4', url: 'https://picsum.photos/400/300?random=4', title: '风景图片四' },
{ id: '5', url: 'https://picsum.photos/400/300?random=5', title: '风景图片五' },
{ id: '6', url: 'https://picsum.photos/400/300?random=6', title: '风景图片六' },
];
interface ImageState {
loading: boolean;
progress: number;
error: boolean;
}
const FastImageListDemo: React.FC = () => {
const [refreshing, setRefreshing] = useState(false);
const [imageStates, setImageStates] = useState<Record<string, ImageState>>({});
useEffect(() => {
FastImage.preload(IMAGE_LIST.map(item => ({ uri: item.url })));
}, []);
const handleLoadStart = useCallback((id: string) => {
setImageStates(prev => ({
...prev,
[id]: { loading: true, progress: 0, error: false },
}));
}, []);
const handleProgress = useCallback((id: string, event: OnProgressEvent) => {
const { loaded, total } = event.nativeEvent;
const progress = total > 0 ? Math.round((loaded / total) * 100) : 0;
setImageStates(prev => ({
...prev,
[id]: { ...prev[id], progress, loading: true },
}));
}, []);
const handleLoad = useCallback((id: string, event: OnLoadEvent) => {
const { width, height } = event.nativeEvent;
console.log(`图片 ${id} 加载成功: ${width}x${height}`);
setImageStates(prev => ({
...prev,
[id]: { loading: false, progress: 100, error: false },
}));
}, []);
const handleError = useCallback((id: string) => {
console.log(`图片 ${id} 加载失败`);
setImageStates(prev => ({
...prev,
[id]: { loading: false, progress: 0, error: true },
}));
}, []);
const handleRefresh = useCallback(() => {
setRefreshing(true);
FastImage.clearMemoryCache()
.then(() => {
setImageStates({});
})
.finally(() => {
setRefreshing(false);
});
}, []);
const renderImageItem = ({ item }: { item: ImageItem }) => {
const state = imageStates[item.id] || { loading: false, progress: 100, error: false };
return (
<View style={styles.imageCard}>
<View style={styles.imageContainer}>
<FastImage
source={{ uri: item.url }}
style={styles.image}
resizeMode={FastImage.resizeMode.cover}
onLoadStart={() => handleLoadStart(item.id)}
onProgress={(e) => handleProgress(item.id, e)}
onLoad={(e) => handleLoad(item.id, e)}
onError={() => handleError(item.id)}
/>
{state.loading && (
<View style={styles.overlay}>
<ActivityIndicator size="large" color="#007AFF" />
<Text style={styles.progressText}>{state.progress}%</Text>
</View>
)}
{state.error && (
<View style={styles.overlay}>
<Text style={styles.errorText}>加载失败</Text>
<TouchableOpacity style={styles.retryButton}>
<Text style={styles.retryText}>点击重试</Text>
</TouchableOpacity>
</View>
)}
</View>
<Text style={styles.title}>{item.title}</Text>
</View>
);
};
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>FastImage 图片列表</Text>
<TouchableOpacity onPress={handleRefresh}>
<Text style={styles.clearButton}>清理缓存</Text>
</TouchableOpacity>
</View>
<FlatList
data={IMAGE_LIST}
keyExtractor={(item) => item.id}
renderItem={renderImageItem}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
contentContainerStyle={styles.listContent}
showsVerticalScrollIndicator={false}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F5F5',
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 16,
backgroundColor: '#FFFFFF',
borderBottomWidth: 1,
borderBottomColor: '#E0E0E0',
},
headerTitle: {
fontSize: 18,
fontWeight: '600',
color: '#333333',
},
clearButton: {
fontSize: 14,
color: '#007AFF',
},
listContent: {
padding: 12,
},
imageCard: {
backgroundColor: '#FFFFFF',
borderRadius: 12,
marginBottom: 12,
overflow: 'hidden',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
imageContainer: {
position: 'relative',
},
image: {
width: '100%',
height: 200,
backgroundColor: '#F0F0F0',
},
overlay: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
},
progressText: {
marginTop: 8,
fontSize: 14,
color: '#007AFF',
fontWeight: '500',
},
errorText: {
fontSize: 16,
color: '#FF3B30',
marginBottom: 12,
},
retryButton: {
paddingHorizontal: 16,
paddingVertical: 8,
backgroundColor: '#007AFF',
borderRadius: 8,
},
retryText: {
fontSize: 14,
color: '#FFFFFF',
},
title: {
padding: 12,
fontSize: 16,
fontWeight: '500',
color: '#333333',
},
});
export default FastImageListDemo;
⚠️ 七、注意事项与常见问题
7.1 HarmonyOS 平台限制
| 属性/方法 | 支持情况 | 说明 |
|---|---|---|
| source.priority | ❌ 不支持 | 加载优先级暂未实现 |
| source.cache | ❌ 不支持 | 缓存策略暂未实现 |
| 其他属性 | ✅ 支持 | 功能完整 |
7.2 常见问题 FAQ
Q1: 图片不显示怎么办?
检查以下几点:
- 确认图片 URL 是否正确
- 检查网络权限是否配置
- 查看 onError 回调是否有错误信息
- 尝试清除缓存后重新加载
Q2: 列表滑动卡顿如何优化?
tsx
// 1. 使用 getItemLayout 优化
<FlatList
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
/>
// 2. 设置合适的 initialNumToRender
<FlatList initialNumToRender={5} />
// 3. 使用 keyExtractor 提供稳定的 key
<FlatList keyExtractor={(item) => item.id} />
Q3: 如何处理 HTTPS 证书问题?
在开发环境可以临时忽略证书验证,生产环境需要正确配置证书:
tsx
// 仅用于开发调试
source={{
uri: 'https://example.com/image.jpg',
headers: { 'Accept-Encoding': 'gzip' },
}}
7.3 性能优化建议
| 优化项 | 建议 |
|---|---|
| 图片尺寸 | 根据显示尺寸选择合适分辨率,避免加载过大图片 |
| 列表优化 | 使用 getItemLayout、initialNumToRender 等属性 |
| 缓存策略 | 合理使用 preload 预加载关键图片 |
| 内存管理 | 页面卸载时调用 clearMemoryCache |
| 占位图 | 设置 defaultSource 提升用户体验 |
📊 八、总结
8.1 核心要点回顾
- FastImage 底层使用原生图片加载库,性能远超原生 Image
- 支持内存缓存和磁盘缓存,大幅减少网络请求
- 提供完整的加载生命周期回调,便于状态管理
- 预加载功能可提前下载图片,提升用户体验