背景:为什么用 RNOH 适配鸿蒙
作为一个写了三年 React Native 的开发者,笔者一直觉得鸿蒙离自己很远。直到领导在周会上要求 App 适配鸿蒙,才开始查 RNOH。RNOH 是 React Native for OpenHarmony 的缩写,可以理解为让 RN 跑在鸿蒙上的适配层。本文记录从零跑通第一个页面的过程,重点整理环境搭建、项目初始化、组件使用和排障经验。
环境搭建:DevEco Studio、环境变量与 Node 版本
第一步是安装 DevEco Studio。它是鸿蒙官方 IDE,下载速度较慢,笔者花了约两个小时才装好。Mac 上如果之前装过老版本,新版本可能报“应用已损坏”,需要在终端执行:
- xattr -cr /Applications/DevEco-Studio.app
复制代码
第二步是配置环境变量 RNOH_C_API_ARCH=1。官方文档有说明,但笔者一开始没注意,构建时出现 undefined is not an object、Cannot read property of null 等错误,排查后设置该变量、清缓存重新构建才恢复。
- # Windows 环境变量设置
- # 在系统变量里新建 RNOH_C_API_ARCH,值为 1
- # 不设置的话后面会遇到各种奇怪的问题,别问我怎么知道的
- # Mac 环境
- # 编辑 ~/.zshrc,添加:
- export RNOH_C_API_ARCH=1
- # 然后 source ~/.zshrc 生效
复制代码
第三步是安装 Node。RNOH 要求 Node 22.11.0 以上,笔者本地还是 20.x,用 nvm 升级:
如果漏掉 nvm use 22,终端里仍是老版本,后续 npm install 可能报 engine 不兼容。
创建项目与 Metro 配置
用 RN 官方 CLI 创建项目:
- npx @react-native-community/cli init MyHarmonyApp
复制代码
然后安装鸿蒙适配依赖:
- npm install @react-native-oh/react-native-harmony
- npm install @react-native-oh/react-native-harmony-cli
复制代码
项目目录中,harmony 是鸿蒙工程核心,关键路径包括 harmony/entry/src/main/ets(ArkTS 代码)、cpp(C++ 桥接代码)、resources(资源文件),以及 build-profile.json5、oh-package.json5。src 目录放 RN 业务代码,App.tsx 是 RN 入口,metro.config.js 是 Metro 打包配置。
metro.config.js 必须加入 RNOH 的 Harmony Metro 配置,否则 Metro 能启动,但打包出的 bundle 在鸿蒙上会白屏:
- const {mergeConfig, getDefaultConfig} = require('@react-native/metro-config');
- const {createHarmonyMetroConfig} = require('@react-native-oh/react-native-harmony/metro.config');
- const config = {
- transformer: {
- getTransformOptions: async () => ({
- transform: {
- experimentalImportSupport: false,
- inlineRequires: true,
- },
- }),
- },
- };
- module.exports = mergeConfig(
- getDefaultConfig(__dirname),
- createHarmonyMetroConfig({
- reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
- }),
- config
- );
复制代码
首次在 DevEco Studio 点运行,hvigor 构建会非常慢,笔者等了近五分钟,进度条几乎不动;第二次之后增量编译约十几秒。首次构建建议预留耐心。
第一个页面:计数器 Demo 验证基础 API
页面需求是计数器,点按钮加减、换背景色、弹窗。用到的 RN API 包括 useState 管理状态、Pressable 处理点击、Alert 弹窗、View/Text 渲染 UI、StyleSheet 写样式,没有第三方库。原文代码较长,核心是:最外层 View 用 justifyContent: 'center' 垂直居中;卡片内放标题、圆形计数器、按钮行、额外操作按钮和提示文字;计数按钮通过 setCount 更新,颜色按钮从预设色数组随机取值,弹窗按钮调用 Alert.alert。
这些基础 API 在鸿蒙上最终都跑通了,但过程中遇到不少兼容和工程配置问题。
六个典型踩坑与排障
坑 1:Pressable 的 onPress 不生效
最初用 TouchableOpacity,鸿蒙上点击没反应。社区反馈鸿蒙上 TouchableOpacity 有时有问题,建议用 Pressable。替换后点击恢复正常。
- // 错误写法:在鸿蒙上可能不生效
- <TouchableOpacity onPress={handlePress}>
- <Text>点我</Text>
- </TouchableOpacity>
- // 正确写法:用 Pressable 更稳
- <Pressable onPress={handlePress}>
- <Text>点我</Text>
- </Pressable>
复制代码
坑 2:Alert 弹窗样式不对
Alert.alert() 在鸿蒙上能用,但样式与 iOS、Android 差别较大,按钮文字颜色很淡,几乎看不清。原因是鸿蒙系统默认主题导致,目前 RN 的 Alert 在鸿蒙上适配还不完善。如果对样式有要求,需要自己写 Modal 组件替代。
坑 3:热更新偶尔失灵
开发中热更新有时失效:改了代码,Metro 提示编译成功,但设备页面不刷新。排查后确认是 DevEco Studio 缓存问题,需要手动清缓存再重新构建。
坑 4:SafeAreaView 不好使
在 iOS 上常用 SafeAreaView 处理刘海屏和底部安全区域,但在鸿蒙上行为奇怪,内容可能被顶到状态栏下面。鸿蒙上需要自己处理避让区域,或使用 RNOH 提供的 shim 方案;具体方案要看 RNOH 版本,建议查官方文档。
坑 5:构建 HAP 时签名问题
构建 HAP 时一直报签名错误,例如“签名文件不存在”或“签名验证失败”。鸿蒙应用必须签名才能安装到设备上,不像 Android 开发可以直接装 debug 版本。解决方式是在 DevEco Studio 的 Project Structure 中勾选“Automatically generate signature”,登录华为账号自动生成签名。
坑 6:bundle 文件放错位置
打包完 JS bundle 后,需要放到鸿蒙工程指定位置。笔者一开始放到 harmony/entry/src/main/resources/rawfile/,应用仍白屏。后来发现 bundle 文件名必须与鸿蒙代码里配置的一致,默认是 bundle.harmony.js;如果改名,鸿蒙侧代码也要同步修改。
- // harmony/entry/src/main/ets/pages/Index.ets
- // 这里的 bundle 文件名要跟你打包出来的文件名一致
- new ResourceJSBundleProvider(
- this.rnohCoreContext.uiAbilityContext.resourceManager,
- 'bundle.harmony.js' // 这个名字要对上
- )
复制代码
验证结果与问题归类
最终计数器页面跑通,验证了 useState 正常工作、Pressable 点击事件正常触发、Alert 弹窗能弹出但样式较丑、StyleSheet 样式渲染没问题。踩坑主要集中在四类:组件兼容性(如 TouchableOpacity 点击可能失效,优先 Pressable)、系统 API 差异(Alert 样式与预期不同)、热更新不稳定(需手动清缓存)、安全区域处理(SafeAreaView 可能需要特殊处理)。
给 RN 开发者的适配建议
第一,不要一步到位。先写最简单的页面跑通,确认环境没问题,再逐步加功能。第二,多看 RNOH 官方文档和 GitHub Issues,很多问题社区已经有人踩过。第三,保持耐心。鸿蒙 RN 开发还在快速发展,部分能力不如 iOS、Android 成熟,但 RNOH 更新勤快,基本每月都有新版本。笔者开始用的是 0.72 版本,现已到 0.84,不少早期坑已被修复。
架构上也要取舍:如果只做鸿蒙,直接用 ArkTS 写更顺畅;但 App 要同时跑 iOS、Android、鸿蒙三个平台,维护三套代码成本太高。用 RN 可以复用业务逻辑,只有平台特定功能才写原生代码,长远看更划算。
后续计划
这个计数器 Demo 只是开胃菜。后续计划继续探索 TurboModule(RN 调用鸿蒙原生模块)、TTS 语音播报(Core Speech Kit)、OCR 文字识别(视觉能力)、小艺智能体(通过 Agent Framework Kit 拉起小艺),并分享实现过程和踩坑记录。 |