HarmonyOS APP开发---“图迹“旅行相册App,需要用到这个库

HarmonyOS APP开发---"图迹"旅行相册App,需要用到这个库

做一个旅行相册 App,用户上传的几百张旅途照片要瀑布流展示,点击大图要秒开。RN 自带 Image 没缓存、每次重新下载------@react-native-ohos/react-native-fast-image 的二级缓存 + 预加载,让相册体验追平原生。
📦 仓库地址:https://gitcode.com/CPF-RN/rntpc_react-native-fast-image | 安装:npm install @react-native-ohos/react-native-fast-image


写在前面

"图迹"的核心体验是"滑到就看到"------用户打开某次旅行的相册,几十张照片瀑布流排列,滑到哪里哪里就秒出图。点开大图,几乎无延迟地全屏展示。

这对图片加载的要求极高:

  • 缓存是刚需:滑过的图再滑回来不能重新下载,否则流量爆炸 + 体验卡顿
  • 大图预加载:用户看缩略图时,大图已经在后台拉好了,点开即出
  • 优先级:当前可见区域的图优先加载,不可见区域降级

RN 自带 Image 组件在鸿蒙上没有原生磁盘缓存,每次都要重新下载。react-native-fast-image 底层适配了 HarmonyOS 原生缓存引擎------内存缓存 + 磁盘缓存 + 优先级队列 + 预加载

这篇文章聊什么

  1. 瀑布流加载------FastImage + FlatList 瀑布流
  2. 大图预加载------缩略图加载时预加载高清大图
  3. 优先级控制------可见区域优先、不可见区域降级

#mermaid-svg-TVH5moKzuaMudmST{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TVH5moKzuaMudmST .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TVH5moKzuaMudmST .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TVH5moKzuaMudmST .error-icon{fill:#552222;}#mermaid-svg-TVH5moKzuaMudmST .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TVH5moKzuaMudmST .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TVH5moKzuaMudmST .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TVH5moKzuaMudmST .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TVH5moKzuaMudmST .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TVH5moKzuaMudmST .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TVH5moKzuaMudmST .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TVH5moKzuaMudmST .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TVH5moKzuaMudmST .marker.cross{stroke:#333333;}#mermaid-svg-TVH5moKzuaMudmST svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TVH5moKzuaMudmST p{margin:0;}#mermaid-svg-TVH5moKzuaMudmST .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TVH5moKzuaMudmST .cluster-label text{fill:#333;}#mermaid-svg-TVH5moKzuaMudmST .cluster-label span{color:#333;}#mermaid-svg-TVH5moKzuaMudmST .cluster-label span p{background-color:transparent;}#mermaid-svg-TVH5moKzuaMudmST .label text,#mermaid-svg-TVH5moKzuaMudmST span{fill:#333;color:#333;}#mermaid-svg-TVH5moKzuaMudmST .node rect,#mermaid-svg-TVH5moKzuaMudmST .node circle,#mermaid-svg-TVH5moKzuaMudmST .node ellipse,#mermaid-svg-TVH5moKzuaMudmST .node polygon,#mermaid-svg-TVH5moKzuaMudmST .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TVH5moKzuaMudmST .rough-node .label text,#mermaid-svg-TVH5moKzuaMudmST .node .label text,#mermaid-svg-TVH5moKzuaMudmST .image-shape .label,#mermaid-svg-TVH5moKzuaMudmST .icon-shape .label{text-anchor:middle;}#mermaid-svg-TVH5moKzuaMudmST .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TVH5moKzuaMudmST .rough-node .label,#mermaid-svg-TVH5moKzuaMudmST .node .label,#mermaid-svg-TVH5moKzuaMudmST .image-shape .label,#mermaid-svg-TVH5moKzuaMudmST .icon-shape .label{text-align:center;}#mermaid-svg-TVH5moKzuaMudmST .node.clickable{cursor:pointer;}#mermaid-svg-TVH5moKzuaMudmST .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TVH5moKzuaMudmST .arrowheadPath{fill:#333333;}#mermaid-svg-TVH5moKzuaMudmST .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TVH5moKzuaMudmST .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TVH5moKzuaMudmST .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TVH5moKzuaMudmST .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TVH5moKzuaMudmST .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TVH5moKzuaMudmST .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TVH5moKzuaMudmST .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TVH5moKzuaMudmST .cluster text{fill:#333;}#mermaid-svg-TVH5moKzuaMudmST .cluster span{color:#333;}#mermaid-svg-TVH5moKzuaMudmST div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TVH5moKzuaMudmST .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TVH5moKzuaMudmST rect.text{fill:none;stroke-width:0;}#mermaid-svg-TVH5moKzuaMudmST .icon-shape,#mermaid-svg-TVH5moKzuaMudmST .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TVH5moKzuaMudmST .icon-shape p,#mermaid-svg-TVH5moKzuaMudmST .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TVH5moKzuaMudmST .icon-shape .label rect,#mermaid-svg-TVH5moKzuaMudmST .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TVH5moKzuaMudmST .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TVH5moKzuaMudmST .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TVH5moKzuaMudmST :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



打开旅行相册
加载缩略图列表
缓存命中?
秒出图
网络下载
存入磁盘缓存
存入内存缓存
预加载相邻大图
用户点击
大图缓存命中?
秒开大图
快速下载


第一步:安装

bash 复制代码
npm install @react-native-ohos/react-native-fast-image

第二步:瀑布流照片列表

tsx 复制代码
import FastImage from '@react-native-ohos/react-native-fast-image'
import { FlatList, View, Text, Dimensions } from 'react-native'

const { width } = Dimensions.get('window')
const COLUMN_WIDTH = (width - 24) / 2 // 两列瀑布流

interface Photo {
  id: string
  thumb: string  // 缩略图 URL
  full: string   // 高清大图 URL
  title: string
}

function PhotoCard({ photo }: { photo: Photo }) {
  return (
    <View style={{ width: COLUMN_WIDTH, marginBottom: 12 }}>
      <FastImage
        style={{ width: '100%', height: COLUMN_WIDTH * 1.3, borderRadius: 8 }}
        source={{
          uri: photo.thumb,
          priority: FastImage.priority.normal,
          cache: FastImage.cacheControl.immutable, // 永不重复下载
        }}
        resizeMode={FastImage.resizeMode.cover}
      />
      <Text style={{ fontSize: 13, color: '#666', marginTop: 4 }}>{photo.title}</Text>
    </View>
  )
}

// 瀑布流列表
<FlatList
  data={photos}
  numColumns={2}
  keyExtractor={(item) => item.id}
  renderItem={({ item }) => <PhotoCard photo={item} />}
  contentContainerStyle={{ padding: 12 }}
/>

cache: FastImage.cacheControl.immutable 是关键------图片 URL 不变就永不重新请求,旅行照片一旦上传就不会变,完美匹配这个策略。


第三步:大图预加载

用户在看缩略图列表时,提前把当前可见区域相邻的几张高清大图拉进缓存:

tsx 复制代码
import { useEffect, useRef } from 'react'

function usePreloadFullImages(photos: Photo[], visibleStart: number, visibleEnd: number) {
  useEffect(() => {
    // 预加载当前可见区域 + 前后各 2 张的大图
    const toPreload: Array<{ uri: string; priority: any }> = []
    for (let i = Math.max(0, visibleStart - 2); i <= Math.min(photos.length - 1, visibleEnd + 2); i++) {
      toPreload.push({
        uri: photos[i].full,
        priority: FastImage.priority.low, // 预加载用低优先级,不抢当前图
      })
    }
    FastImage.preload(toPreload)
  }, [visibleStart, visibleEnd])
}

预加载用 priority.low------不抢当前可见缩略图的带宽,等缩略图都加载完了再拉大图。


第四步:点击查看大图(秒开)

tsx 复制代码
function FullScreenViewer({ photo, onClose }: { photo: Photo; onClose: () => void }) {
  return (
    <View style={{ flex: 1, backgroundColor: '#000' }}>
      <FastImage
        style={{ flex: 1 }}
        source={{
          uri: photo.full,
          priority: FastImage.priority.high, // 大图用高优先级
          cache: FastImage.cacheControl.immutable,
        }}
        resizeMode={FastImage.resizeMode.contain}
        onLoad={(e) => {
          console.log(`大图加载完成: ${e.nativeEvent.width}x${e.nativeEvent.height}`)
        }}
      />
      <TouchableOpacity onPress={onClose} style={{ position: 'absolute', top: 40, right: 20 }}>
        <Text style={{ color: '#fff', fontSize: 18 }}>✕</Text>
      </TouchableOpacity>
    </View>
  )
}

因为第三步已经预加载了大图,用户点击时缓存已命中,几乎零延迟秒开


为什么"图迹"选了 FastImage?

需求 RN Image @react-native-ohos/react-native-fast-image
磁盘缓存 ✅ 二级缓存
预加载 preload()
优先级 ✅ low/normal/high
缓存策略 ✅ immutable/web
大图秒开 ❌ 重新下载 ✅ 预加载命中缓存
进度回调 onProgress

总结

"图迹"这个场景里,@react-native-ohos/react-native-fast-image 解决了三件事:

  1. 缓存不重下 ------immutable 策略 + 二级缓存,滑过的图再滑回来秒出
  2. 大图秒开------缩略图加载时低优先级预加载大图,点击即出
  3. 优先级调度------可见区域 normal,预加载 low,大图查看 high,带宽合理分配

如果你也在做相册、电商、社交等大量图片的 RN 鸿蒙应用,react-native-fast-image 是性能体验的底线保障。

相关推荐
2501_9197490321 分钟前
华为鸿蒙免费提醒APP—小羊提醒
华为·harmonyos·鸿蒙
lilian23331 分钟前
HarmonyOS 7 新特性(二十四)|ModularObjectExtensionAbility 与 Taihe IPC
华为·harmonyos
tsqtsqtsq03091 小时前
鸿蒙系统应用市场更新功能详解与开发适配指南
服务器·harmonyos
Georgewu2 小时前
HarmonyOS Dev Assistant (HarmonyOS开发助手)如何打通元服务开发全流程
harmonyos
2501_919749033 小时前
华为鸿蒙免费计算油耗APP—小羊油耗
华为·harmonyos·鸿蒙
less_121383 小时前
HarmonyOS WPS Open SDK:OpenFileRequest 构造参数与 sendRequest 打开链路
华为·sdk·harmonyos·wps·鸿蒙开发·文档编辑
huainingning3 小时前
华三华为锐捷迈普中兴交换机二三层接口切换命令
服务器·网络·华为
贾伟康4 小时前
【时光清单|11】HarmonyOS ArkTS 每日语录实战:从本地仓库稳定生成首页内容
harmonyos·arkts·状态管理·arkui·本地数据
ymwlchina4 小时前
华为手机P40 Plus如何通过adb无线连接
adb·华为·智能手机