从 0 开始在鸿蒙上使用 react-native-webp-format:环境搭建、应用创建、库集成与 6 大场景真机验证
从 0 开始在鸿蒙上使用 react-native-webp-format:环境搭建、应用创建、库集成与 6 大场景真机验证
本文面向想把 React Native 应用跑在 HarmonyOS 上的开发者,以 react-native-webp-format(已鸿蒙适配,tag
v1.3.1)为例,走完一条完整的从 0 到 1 路线:RNOH 环境搭建 → 创建 RN 应用 → 集成三方库 → 编写 6 个应用场景案例 → 真机调试验证。全部步骤在 RNOH 0.84 + API 26 真机上实测通过。
一、react-native-webp-format 是什么,能做什么
react-native-webp-format 的定位一句话讲完:让 RN 的 <Image> 组件认识 WebP 格式(含动画 WebP)。
WebP 在同等画质下比 JPEG 小 25%~35%、比 PNG 小 26%,且支持有损/无损/透明通道/动画,是移动端图片流量优化的主力格式。但 RN 的默认图片管线在 iOS / Android 上并不完整支持 WebP:
- iOS:RN 默认图片管线不支持 WebP,这个库通过
RCTImageDataDecoder协议 +SDWebImageWebPCoder注册原生解码器; - Android:依赖 Fresco 的
webpsupport/animated-webp模块补齐解码能力。
而到了鸿蒙上,情况完全不同——ArkUI 的 <Image> 组件原生支持 WebP(官方支持格式清单:png, jpg, jpeg, bmp, svg, webp, gif, heif, tiff,静态与动画 WebP 都支持)。RNOH 的 <Image> 底层渲染走的就是 ArkUI <Image>,所以解码能力是"系统白送"的。
1.1 鸿蒙侧的能力详解
适配后的库在鸿蒙侧提供的能力:
| 能力 | 鸿蒙侧表现 | 是否需要 JS 代码 |
|---|---|---|
| 静态 WebP 渲染 | ArkUI 原生解码,require('./x.webp') 直接用 | 不需要 |
| 动画 WebP 播放 | ArkUI 原生逐帧播放 | 不需要 |
| 网络 WebP(uri) | HTTP 拉取 + 原生解码 | 不需要 |
| ImageBackground 背景图 | 同样直接支持 .webp 源 | 不需要 |
| resolveAssetSource 元信息 | 可获取 uri / width / height / scale | 不需要 |
| resizeMode 缩放模式 | contain / cover / stretch / center 全支持 | 不需要 |
| TurboModule 兼容层 | ReactNativeWebPFormatPackage 满足 autolinking 契约 | 自动注册 |
两个必须知道的差异点:
Image.getSize()对 WebP 不工作,统一用Image.resolveAssetSource()替代(本文场景 5 演示);- 多个 ~5MB 的动画 WebP 同时渲染可能 OOM,大图场景注意控制数量。
另外,鸿蒙侧 Platform.OS 返回 'android',库在所有平台上工作方式一致,JS 侧不需要写平台分支。
1.2 三平台实现对比(为什么鸿蒙适配"极小")
| 平台 | 实现方式 | 工作量 |
|---|---|---|
| iOS | RCTImageDataDecoder + SDWebImageWebPCoder | 大(解码器注册、CocoaPods 集成) |
| Android | Fresco webpsupport + animated-webp | 中(gradle 依赖、解码器注册) |
| HarmonyOS | ArkUI 原生解码,TurboModule 骨架即最终实现 | 极小 |
这正是这篇适配的经验总结:跨端适配第一步永远是查目标平台的原生能力清单。想了解适配过程的完整踩坑复盘(librnoh_app.so、module.json5 的 skills 缺失"跳设置页"等),见同系列上一篇文章。
二、从 0 搭建 RNOH 开发环境(macOS)
RNOH 0.84 对环境的要求:Node.js ≥ 22.11、DevEco Studio 26.x、OpenHarmony SDK API ≥ 17、pnpm ≥ 10,且 C-API 架构(RNOH_C_API_ARCH=1)是默认架构。
2.1 工具清单
| 工具 | 版本要求 | 安装方式 |
|---|---|---|
| Node.js | ≥ 22.11 | nvm install 22 && nvm use 22 |
| pnpm | ≥ 10 | corepack enable |
| DevEco Studio | 26.x | 官网下载 |
| OpenHarmony SDK | API ≥ 17 | DevEco Studio → Preferences → SDK 勾选安装 |
| hdc | 随 SDK | {SDK}/openharmony/toolchains/ 加入 PATH |
2.2 环境变量(写入 ~/.zshrc)
# RNOH 0.82+ 必需:CAPI 架构开关(缺失会导致运行时回退旧架构甚至崩溃)
export RNOH_C_API_ARCH=1
# hdc 调试端口(推荐)
export HDC_SERVER_PORT=7035
# DevEco SDK 路径(macOS 默认安装路径下,react-native run-harmony 等 CLI 依赖)
export DEVECO_SDK_HOME="/Applications/DevEco-Studio.app/Contents/sdk/default"
source ~/.zshrc 后验证:
node -v # v22.x
pnpm -v # 10+
hdc version # Ver: 3.2.0e
hdc list targets # 应列出你的真机序列号
2.3 网络配置(国内环境)
npm 默认源在国内经常超时,建议配置华为云镜像:
npm config set registry https://repo.huaweicloud.com/repository/npm/
反模式提醒:不要全局关闭
strict-ssl,仅在遇到 SSL 错误时对镜像域名单独处理。
我的环境实测结果:Node 22.22.3、pnpm 12.3.4、DevEco 26.0.0.621、SDK API 26、hdc 3.2.0e,全部达标。
三、创建 RN 应用并接入鸿蒙宿主
3.1 创建 RN 工程
npx @react-native-community/cli init webp-demo --version 0.84.1
cd webp-demo
RN 0.84 工程的 package.json 关键依赖:
{
"dependencies": {
"react": "19.2.3",
"react-native": "0.84.1"
},
"engines": { "node": ">= 22.11.0" }
}
3.2 安装 RNOH 依赖
npm i @react-native-oh/react-native-harmony@^0.84.3
3.3 配置 harmony Metro
metro.config.js 换成鸿蒙版配置(这一步决定 bundle 能以 --platform harmony 打出):
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { createHarmonyMetroConfig } = require('@react-native-oh/react-native-harmony/metro.config');
const config = createHarmonyMetroConfig({
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
});
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
3.4 在 DevEco Studio 中创建鸿蒙工程
在工程根目录新建 harmony/ 目录,用 DevEco Studio 在该目录创建 HarmonyOS 工程(Empty Ability 模板即可),然后手工完善为 RN 宿主。RNOH 宿主"五件套"缺一不可:
| # | 组件 | 说明 |
|---|---|---|
| 1 | EntryAbility 继承 RNAbility | 应用入口 |
| 2 | Index.ets 挂 RNApp | 注册包与 bundle provider |
| 3 | cpp/PackageProvider.cpp + CMakeLists.txt | C++ 胶水层,产出 librnoh_app.so(0.84 C-API 架构必需) |
| 4 | oh-package.json5 依赖 @rnoh/react-native-openharmony | RNOH 运行时 |
| 5 | metro.config.js 接 createHarmonyMetroConfig | Metro 侧支持 |
EntryAbility.ets:
import { RNAbility } from '@rnoh/react-native-openharmony';
export class EntryAbility extends RNAbility {
getPagePath() {
return 'pages/Index';
}
}
pages/Index.ets(双通道 bundle 加载:Metro 优先、离线兜底):
import { RNApp, MetroJSBundleProvider, ResourceJSBundleProvider, AnyJSBundleProvider } from '@rnoh/react-native-openharmony';
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
build() {
Column() {
RNApp({
rnInstanceConfig: {
createRNPackages: (ctx) => [],
enableNDKTextMeasuring: false,
},
appKey: 'webp_demo',
jsBundleProvider: new AnyJSBundleProvider([
// 开发态:Metro 热更新
MetroJSBundleProvider.fromServerIp('localhost', 8083, ['webp_demo']),
// 兜底:打进 HAP 的离线 bundle,脱离电脑也能跑
new ResourceJSBundleProvider(
(getContext(this) as common.UIAbilityContext).resourceManager,
'bundle.harmony.js', ['webp_demo']),
]),
})
}
.width('100%')
.height('100%')
}
}
坑位提醒(来自实测):
module.json5必须有mainElement: "EntryAbility"和pages: "$profile:main_pages",否则启动 401 白屏;- EntryAbility 必须声明
skills(entity.system.home+action.system.home),否则点桌面图标会跳到设置页——aa start -a显式启动验证不出这个问题,只有图标启动才走入口解析;- 纯 ArkTS 宿主在 0.84 跑不起来,C++ 胶水层产出的
librnoh_app.so必须存在。
3.5 生成离线 bundle
npx react-native bundle --platform harmony --dev false \
--entry-file index.js \
--bundle-output harmony/entry/src/main/resources/rawfile/bundle.harmony.js \
--assets-dest harmony/entry/src/main/resources/rawfile/
四、集成 react-native-webp-format
4.1 JS 侧安装
npm i react-native-webp-format@^1.3.1
安装的就是社区同名库的鸿蒙适配版(发布在 oh-react-native/react-native-webp-format),JS 侧 API 与 iOS/Android 完全一致——因为本来就没有 JS API,WebP 支持是自动注册的。
4.2 鸿蒙侧依赖(autolinking)
在 harmony/entry/oh-package.json5 中添加依赖:
{
"name": "entry",
"dependencies": {
"@rnoh/react-native-openharmony": "^0.84.0",
"@react-native-ohos/react-native-webp-format": "^1.3.1"
}
}
然后安装:
cd harmony
ohpm install --all
autolinking 会根据库 package.json 中声明的 harmony.autolinking.ohPackageName(@react-native-ohos/react-native-webp-format)自动完成发现。
4.3 在 RNApp 中注册 Package
pages/Index.ets 的 createRNPackages 中注册:
import { ReactNativeWebPFormatPackage } from '@react-native-ohos/react-native-webp-format';
rnInstanceConfig: {
createRNPackages: (ctx) => [
new ReactNativeWebPFormatPackage(ctx),
],
enableNDKTextMeasuring: false,
},
就这么多。没有解码器注册、没有 gradle/pod 配置、没有 JS 初始化——鸿蒙侧适配版把 iOS/Android 上"注册第三方解码器"的全部复杂度都消化掉了。
4.4 手动接入(可选)
如果宿主工程不走 autolinking,也可以手动接入:
- 将库仓库
harmony/react_native_webp_format目录复制到鸿蒙模块; - 在
oh-package.json5中添加该模块依赖(本地路径或构建出的 HAR); - 在
RNOHProvider/createRNPackages中手动new ReactNativeWebPFormatPackage(ctx)。
4.5 准备 WebP 素材
把 WebP 图片放进 RN 工程(本文 Demo 素材来自库仓库 example/assets/):
webp-demo/
├── App.tsx
├── assets/
│ ├── 1.sm.webp # 静态 WebP
│ ├── 3.sm.webp # 静态 WebP(另一张)
│ └── animated-webp-supported.webp # 动画 WebP
└── harmony/...
五、6 大应用场景案例
Demo 用一个 Tab 容器组织 6 个场景,每个场景对应一个真实业务里会遇到的用法。完整代码见配套仓库 example/App.tsx,下面按场景拆解。
场景 1:静态 WebP(本地资源)
最基础的用法——和 PNG/JPG 完全一样的 require 写法:
<Image source={require('./assets/1.sm.webp')} style={styles.image} />
不需要 import 任何额外模块,不需要初始化调用。适用场景:App 内置的插画、图标、客服二维码等用 WebP 减包体积。

场景 2:动画 WebP(自动播放)
<Image source={require('./assets/animated-webp-supported.webp')} style={styles.image} />
动画 WebP 由 ArkUI Image 原生逐帧解码播放,自动循环,JS 侧无任何播放控制代码。适用场景:加载动画、直播礼物动效、运营活动动图——以前这些只能用 GIF 或 Lottie,现在一张动画 WebP 就够了(体积通常比 GIF 小一半以上)。

场景 3:网络 WebP(uri 加载)
const [loaded, setLoaded] = useState(false);
<Image
source={{uri: 'https://www.gstatic.com/webp/gallery/2.jpg'}}
style={styles.image}
onLoad={() => setLoaded(true)}
onError={() => setLoaded(false)}
/>
网络 WebP 走 RNOH 的图片管线(HTTP 拉取 → ArkUI 解码),onLoad / onError 回调正常触发。适用场景:CDN 分发的商品图、 feeds 流图片——服务端出 WebP,客户端直接渲染,省流量不动代码。

场景 4:ImageBackground 背景图
<ImageBackground
source={require('./assets/3.sm.webp')}
style={styles.imageBackground}>
<Text style={styles.description}>WebP 作为背景,文本正常叠加</Text>
</ImageBackground>
ImageBackground 同样直接吃 .webp 源,子组件叠加正常。适用场景:卡片头图、 banner、海报文字排版。

场景 5:resolveAssetSource 读取元信息
const source = Image.resolveAssetSource(require('./assets/1.sm.webp'));
// source.uri -> 'asset://assets/1.sm.webp'
// source.width -> 320
// source.height -> 214
// source.scale -> 1
⚠️ 这是鸿蒙侧最重要的差异点:
Image.getSize()对 WebP 不工作,需要图片尺寸时统一用resolveAssetSource()。这个方法同步返回资源元信息,不依赖解码完成,比getSize()的回调式 API 用起来更顺手。
适用场景:根据图片原始宽高比计算容器尺寸、埋点上报图片信息。

场景 6:resizeMode 四种缩放模式
{['contain', 'cover', 'stretch', 'center'].map(mode => (
<Image
key={mode}
source={require('./assets/3.sm.webp')}
style={styles.resizeImage}
resizeMode={mode}
/>
))}
四种模式在鸿蒙侧全部正常工作。适用场景:同一张运营图要适配不同比例坑位时的行为对齐验证。

Tab 容器骨架
const SCENES = [
{key: 'static', title: '静态', Comp: StaticWebPScreen},
{key: 'animated', title: '动画', Comp: AnimatedWebPScreen},
{key: 'remote', title: '网络', Comp: RemoteWebPScreen},
{key: 'background', title: '背景图', Comp: BackgroundWebPScreen},
{key: 'source', title: '元信息', Comp: AssetSourceScreen},
{key: 'resize', title: '缩放', Comp: ResizeModeScreen},
];
function App() {
const [active, setActive] = useState(SCENES[0].key);
const ActiveComp = SCENES.find(s => s.key === active)!.Comp;
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>react-native-webp-format 鸿蒙 Demo</Text>
<ScrollView horizontal style={styles.tabBar}>
{SCENES.map(s => (
<Pressable key={s.key} onPress={() => setActive(s.key)}
style={[styles.tab, active === s.key && styles.tabActive]}>
<Text style={styles.tabText}>{s.title}</Text>
</Pressable>
))}
</ScrollView>
<ScrollView contentContainerStyle={styles.scrollContent}>
<ActiveComp />
</ScrollView>
</SafeAreaView>
);
}
选型提醒:Demo 里用 RN 内置
SafeAreaView而不是react-native-safe-area-context——后者没有鸿蒙适配版本,RNOH 找不到对应 ComponentJSIBinder 时会整树静默白屏。选 JS 依赖前先到 oh-react-native 组织查一下有没有鸿蒙适配版。
六、真机调试与验证
6.1 构建与安装
cd harmony
hvigorw assembleHap --mode module -p product=default --no-daemon
构建产物在 entry/build/default/outputs/default/entry-default-signed.hap,推到真机:
hdc install -r entry-default-signed.hap
6.2 启动应用(走用户真实路径)
# 唤醒并解锁屏幕(开发者模式下系统无法自动解锁,需模拟上滑)
hdc shell "power-shell wakeup"
hdc shell "uitest uiInput swipe 576 2400 576 600 600"
# 先确认包名(AppScope/app.json5 里的 bundleName)
hdc shell aa start -b com.example.smarttoolbox -a EntryAbility
hdc shell "pidof com.example.smarttoolbox" # 有 pid = 启动成功
调试命令速查:
命令 用途 hdc list targets确认真机连接 hdc shell aa start -b <bundle> -a <ability>启动应用 hdc shell "uitest dumpLayout -p /data/local/tmp/l.json"导出 UI 树(验证文本/组件渲染) hdc shell "uitest uiInput click <x> <y>"模拟点击 hdc shell snapshot_display -f /data/local/tmp/s.jpeg截屏 hdc file recv /data/local/tmp/s.jpeg ./s.jpeg拉取文件到本机 hilog | grep <关键字>查日志
6.3 六大场景真机验证结果
通过 uitest dumpLayout 读取 UI 树文本 + 截图像素分析(非白像素占比)双重验证,六个场景全部通过:
| 场景 | UI 树验证 | 像素分析 | 结果 |
|---|---|---|---|
| 1. 静态 WebP | 标题/说明文本齐全 | 53.2% 非白 | ✅ |
| 2. 动画 WebP | 逐帧播放中 | 57.1% 非白 | ✅ |
| 3. 网络 WebP | onLoad 已触发 | 80.2% 非白 | ✅ |
| 4. ImageBackground | 文本叠加正常 | 48.1% 非白 | ✅ |
| 5. resolveAssetSource | uri: asset://assets/1.sm.webp,320 × 214,scale: 1 | — | ✅ |
| 6. resizeMode | 四格渲染 | 47.2% 非白 | ✅ |
场景 5 的实测输出(来自真机 UI 树原文):
uri: asset://assets/1.sm.webp
width × height: 320 × 214
scale: 1
所有截图(article/screenshots/)与验证数据均来自 HarmonyOS 真机(API 26,RNOH 0.84.3,Hermes 引擎)。
5.4 真机调试的几个实战要点
- 锁屏是第一个拦路虎:开发者模式下
aa start会报10106102 screen is locked——先power-shell wakeup再uitest uiInput swipe上滑解锁; - 验证走用户路径:桌面图标启动 ≠
aa start -a显式启动。module.json5缺skills声明时,显式启动一切正常,图标点击却会跳设置页; - UI 树比截图更可信:
uitest dumpLayout能读到组件文本与 bounds,白屏问题(截图像素 99% 白)用它一眼定位; - Metro 端口连通 ≠ 端口正确:真机连 Metro 前先
lsof -nP -iTCP:8083 -sTCP:LISTEN确认监听进程是 Metro 而不是别的服务; - 离线 bundle 是演示保命符:
AnyJSBundleProvider双通道(Metro 优先 + rawfile 兜底)让应用脱离电脑也能跑,本文所有真机验证都是离线 bundle 跑的。
七、总结
从 0 到 1 在鸿蒙上用起 react-native-webp-format,全流程只有五步:
环境检测通过(Node 22 / DevEco 26 / API 26 / hdc)
↓
创建 RN 0.84 工程 + harmony Metro 配置
↓
鸿蒙宿主五件套(RNAbility / RNApp / C++ 胶水层 / oh-package / metro)
↓
集成库(npm i + ohpm install + createRNPackages 注册,三行核心代码)
↓
hvigorw 构建 → hdc install → 真机 6 场景全通过
回顾全文,鸿蒙侧使用这个库的核心认知有三条:
- 能力是白送的:ArkUI
<Image>原生支持静态与动画 WebP,适配版的 TurboModule 只是 autolinking 契约骨架,JS 侧零改动、零学习成本; - API 差异只有一处:
Image.getSize()换成Image.resolveAssetSource(),其余<Image>/<ImageBackground>用法与 iOS/Android 完全一致; - 真机验证要覆盖真实路径:锁屏解锁、图标启动、UI 树 + 像素双验证,这些"用户视角"的检查项比
aa start显式启动更有说服力。
如果你的 RN 应用已经用了这个库(或任何依赖 WebP 解码的图片库),迁移到鸿蒙的成本几乎为零——npm i + ohpm install + 一行 Package 注册,就能让 WebP 在鸿蒙上跑起来。
参考资料
- 配套仓库:oh-react-native/react-native-webp-format(tag
v1.3.1) - RNOH 官方仓库:oh-react-native
- ArkUI Image 组件文档(支持格式清单)
- 同系列上一篇:React Native 三方库鸿蒙化实战:react-native-webp-format 适配全记录
如有问题欢迎评论区交流,也可以在 AtomGit 仓库提 issue。
社区
欢迎加入 RN 鸿蒙生态社区,共建三方库适配:
更多推荐


所有评论(0)