ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

React Native接入鸿蒙:鸿组件开发与跨端落地实践

React Native接入鸿蒙:鸿组件开发与跨端落地实践 React Native 和鸿蒙HarmonyOS放在一起很多人第一反应是“能跑吗”先说结论能跑而且已经有不少团队把 RN 应用搬上了鸿蒙应用市场。我最近就在做基于 React Native 的业务在鸿蒙设备上的落地整个过程谈不上轻松但把“鸿蒙组件怎么设计”“RN 和鸿蒙怎么通信”“Bundle 怎么打进 HAP”这几件事全走通之后回头看其实有条很清晰的路径。标题里的“鸿组件”我理解为鸿蒙组件但要实现它至少要摸清两个层面鸿蒙开发本身的几个基础概念以及 React Native 跨端技术在鸿蒙上的运行机制。这篇文章就从这两个层面展开中间会穿插我实际集成时的配置、命令和踩坑记录。适合两类人看已经有 React Native 项目、想增加鸿蒙端发布的团队或者已经有鸿蒙原生应用、想用 RN 低成本做页面的同学。两种视角我都覆盖到。1. 项目解读React Native 里的“鸿组件”到底指什么1.1 标题背后的人在解决什么把“在 React Native 中开发鸿组件”这句话拆开看本质上是在处理一个跨端工程问题你手里可能已经有一套 React Native 的代码资产希望它能直接跑在鸿蒙设备上也可能你站在鸿蒙原生这边想把 React Native 的页面作为一个可复用的“组件”嵌进自己的应用里。这两类需求在业务里都很常见。前者的典型场景是公司已有 App 覆盖 Android 和 iOS客户要求鸿蒙版本你不想再配一队原生开发从头写后者的典型场景是鸿蒙原生应用已经做完了框架和核心流程但运营页、营销页、活动页这种高频变动的模块用原生 ArkTS 开发根本跟不上运营节奏而 RN 恰好擅长这种“页面级快速交付”。我把两种场景的解决路径放在一起讲是因为底层机制完全一样JS Bundle 必须能在鸿蒙上执行React Native 的组件树必须能映射到 ArkUI 的界面层原生 API 必须能通过桥接层暴露给 JS。把这条主链路打通不管你是“RN 包裹鸿蒙”还是“鸿蒙包裹 RN”都能自己组装出来。1.2 三种接法对应三种业务场景实际操作时接入形态基本就三种鸿蒙工程为主RN 页面作为内嵌组件。鸿蒙原生工程负责应用壳、系统能力、导航框架RN 只做某几个页面。这种接法对原生团队最友好风险最小。RN 工程为主鸿蒙作为新的目标平台。RN 的 JS 代码继续作为业务主开发语言鸿蒙端通过适配包RNOH作为运行容器之一和 Android、iOS 并列存在。RN 页面调用鸿蒙原生能力组件。这是更进阶的玩法把鸿蒙的扫码、蓝牙、分布式流转等系统能力封装成原生模块让 JS 通过统一的接口调用不关心底层到底是哪个系统。很多人在网上搜“react native 鸿蒙”看到的都是第二种形态但真实项目里反而是第一种混合形态落地最快。RN 再强大也不可能在短期内覆盖所有鸿蒙原生能力所以“原生主壳 RN 业务页”的模式更务实。1.3 为什么选 RN而不是全员转原生这个问题我每次跟团队沟通都会被问到。我通常不算技术账先算人力账一个原生鸿蒙页面从设计稿到稳定上线至少需要 ArkTS 开发者一周时间同样的页面用 RN 写前端熟练工一到两天就能拉出可交互版本。RN 的组件生态、状态管理库、热更新方案都是现成的鸿蒙原生社区里很多能力还在“看着文档干活”的阶段。当然这不意味着 ArkTS 原生不重要。恰恰相反RN 在鸿蒙上能跑起来靠的正是底层 ArkUI 的原生渲染和系统能力调用。两者是协作关系不是替代关系。如果你是纯鸿蒙原生团队可以等一下再上 RN但如果你已经背着安卓和 iOS 两套包袱RN 接鸿蒙几乎是最平滑的那条路。2. 鸿蒙开发的基础知识别被新概念吓住2.1 HarmonyOS、OpenHarmony、鸿蒙 NEXT 的关系很多 RN 开发者第一次接触鸿蒙会被一堆名词搞迷糊。我尽量用大白话讲清楚。HarmonyOS 是华为面向消费者设备提供的商业操作系统版本迭代到 NEXT 之后底层架构做了大调整变成了不依赖安卓开源项目的全新系统。OpenHarmony 则是开放源代码项目不同设备厂商可以基于它做自己的发行版。对普通开发者来说你写代码时用的 SDK、IDE、模拟器统称属于鸿蒙开发生态不必在发行版名字上纠结。真正要关心的是 API Level。RNOH 适配包会标明自己支持哪个 API Level比如 0.72 版本的适配包可以用在 API 10 到 API 12 的设备上。选版本的时候别盲选最新 SDK要先看 RNOH 的版本映射表否则编译能过运行时各种崩溃。2.2 Stage 模型和 UIAbility应用入口怎么组织鸿蒙应用的应用模型有两种旧一点的是 FA 模型新版本推的是 Stage 模型。RN 开发者不必深究历史只需要知道你用 DevEco Studio 新建鸿蒙工程时选 Stage 模型就对了RNOH 的集成模板也是基于 Stage 模型的。Stage 模型里最核心的概念是 UIAbility。它就是应用的“入口能力”相当于 Android 的 Activity。一个鸿蒙应用可以有一个或多个 UIAbility每个 UIAbility 负责一块完整的功能场景。RN 页面集成进鸿蒙本质就是在某个 UIAbility 的界面里挂载一个 ReactNativeView让它占据一块画布渲染 JS 组件树。2.3 ArkTS 与 ArkUI一张界面是怎么画出来的ArkTS 是鸿蒙的声明式开发语言基于 TypeScript 做了一层约束和扩展。ArkUI 是它的 UI 框架语法风格很像 SwiftUI 或用声明式写法写的 React比如你写Column() { Text(hello) .fontSize(16) .fontColor(#333) Button(点击) .onClick(() doSomething()) }这种写法和 React 的 JSX 在思想上是一致的界面由状态驱动状态变了界面自动更新。RN 开发者上手 ArkTS 的成本比想象中低很多真正需要适应的是组件体系不同以及原生事件和生命周期的命名方式。2.4 DevEco Studio 和配套工具链鸿蒙开发的主 IDE 是 DevEco Studio它基于 IntelliJ 平台用过 Android Studio 的人很快就能上手。集成 RN 之后日常流程基本是DevEco 负责鸿蒙原生工程编译和真机调试Metro 负责 JS 包服务和热更新两者通过 USB 或局域网通信。配套工具还有 ohpm鸿蒙包管理类似 npm、hvigor构建工具类似 Gradle、hdc设备连接工具类似 adb。在集成 RNOH 时ohpm 用来装react-native-ohos/react-native这套原生依赖hvigor 负责把原生代码和 CMake 产物编译成 hap 包hdc 负责把包装到真机上。这套工具链和安卓的 gradle/adb 是一一对应的迁移并不难。3. RN 在鸿蒙上被“架”起来的内幕3.1 RNOH 项目到底是什么RNOHReact Native OpenHarmony是社区和厂商共同维护的适配工程目标是把 React Native 完整跑在 OpenHarmony/HarmonyOS 上。它不是把安卓端的实现搬过来而是利用 RN 架构里“平台层可替换”的设计重新实现了一套鸿蒙平台层。React Native 自身分三层上层是你的 JS/React 代码中层是 C 编写的核心运行时和 Yoga 布局引擎底层是面向不同平台的渲染和桥接。Android 和 iOS 各自有各自的原生实现鸿蒙的原生实现就是 RNOH 提供的。项目维护在 gitee 和 github 的 react-native-ohos 组织下依赖包以react-native-ohos/react-native的形式发布在 npm版本号和 RN 主版本保持同步。3.2 双轨渲染C 布局引擎与 ArkUI 原生控件RN 在鸿蒙上实现渲染的思路我称之为“双轨渲染”。一条轨是主要的RN 的组件树交给 C 层做布局计算Yoga 算 flex然后在 ArkUI 提供的 XComponent 原生存取管道上直接绘制。这样能最大程度复用 RN 的布局引擎保证同一套代码在安卓、iOS、鸿蒙上的视觉表现一致。另一条轨是针对小部分需要系统原生交互的组件比如文本输入框、开关、下拉刷新RNOH 会直接映射成 ArkUI 的原生控件。原因是这类组件牵扯系统输入法、焦点管理、无障碍等复杂能力纯自绘很难做好。这也是为什么 RN 官方文档里常说“少数基础组件在不同平台上会有细微差异。”理解双轨渲染对排查问题很有帮助。如果你发现某个样式的表现在鸿蒙上不对先判断它走的是自绘路径还是原生控件路径再决定是调 RN 样式还是调原生实现。3.3 JSI 和 TurboModuleJS 怎么调鸿蒙能力新版 RN 用的是 JSIJavaScript Interface通信机制。简单说JSI 让 JS 可以直接持有 C 对象的引用同步调用方法不再像老版本桥那样走 JSON 序列化和异步队列性能提升明显。RNOH 在鸿蒙侧完全按新架构来实现原生模块。开发者用 ArkTS 或 C 写原生能力然后通过 TurboModule 的规则注册给 JS调用方就像调用普通 JS 方法一样使用返回 Promise 或同步值都行。标题里说的“鸿组件”在工程上指的就是这种“JS 可调用的鸿蒙原生能力封装”。比如你要把鸿蒙的剪切板能力封装成组件接口流程大致是先写一份 TypeScript 类型声明定义方法签名再在鸿蒙侧实现一个继承约定基类的原生模块最后把它注册到模块工厂里。JS 端拿到模块后调用Clipboard.getText()底层返回的就是鸿蒙原生 API 的结果。3.4 版本对应RN、RNOH、SDK 对齐集成 RNOH 最容易踩的坑就是版本不匹配。我的建议是先查官方版本映射表再决定装什么。目前大致规律是React Native 版本RNOH 包版本常用 HarmonyOS SDK0.71.x0.71.xAPI 9/100.72.x0.72.xAPI 10/120.73.x 及以上看官方发布节奏API 12 及以上版本错位的后果很隐蔽编译可能成功但运行时会报dlopen failed找不到符号或者启动即崩溃。排查这类问题基本只能靠对齐版本没有太多捷径。4. 实操在鸿蒙工程中集成 RN 组件4.1 环境准备清单我先把环境列一张表照着准备不会错依赖建议版本说明Node.js18 LTS 及以上RN 工具链必须DevEco Studio5.x 及以上鸿蒙 IDE自带 SDK 和 hdcHarmonyOS SDK按 RNOH 映射表选不要盲目装最新ohpmDevEco 内置安装鸿蒙开源包JDK17 或 DevEco 要求版本鸿蒙构建依赖准备环境时最好把 RN 的 npm 镜像源和鸿蒙的 ohpm 仓库源都确认能连通。国内网络环境下事先配好镜像能省掉很多安装超时的麻烦。4.2 创建鸿蒙工程并引入依赖打开 DevEco Studio新建一个 Standard 空应用选择 Stage 模型包名比如com.example.rninoh。创建完成后工程目录下会有entry/src/main/ets等模块。接下来在entry/oh-package.json5的 dependencies 里加上 RNOH 依赖{ dependencies: { react-native-ohos/react-native: 0.72.17 } }版本号以 npm 实际可用版本为准。加完依赖后在终端执行ohpm install把原生依赖拉下来。然后打开entry/src/main/module.json5在 module 里增加网络权限{ module: { name: entry, type: entry, requestPermissions: [ { name: ohos.permission.INTERNET } ] } }这一步非常关键尤其是 debug 模式。鸿蒙应用默认没有网络权限漏配会表现为 Metro 连不上、图片加载失败、接口请求全部报错。4.3 配置项目让 RN 跑起来在entry/build-profile.json5里增加 RN 相关配置。以 0.72 版本为参考大致是这样{ apiType: stageMode, buildOption: { externalNativeOptions: { path: ./src/main/cpp/CMakeLists.txt, arguments: , abiFilters: [arm64-v8a] } }, reactNative: { surfaceName: RNAppSurface, moduleName: RNApp, entryPath: index.js, bundleDebugPath: assets://index.bundle.harmony.js, bundleReleasePath: rawfile/index.bundle.harmony.js } }字段名在不同 RNOH 版本里可能略有差异但核心就几个surfaceName 是鸿蒙侧挂载时用的表面名称entryPath 告诉构建系统 JS 入口文件在哪bundle 路径是 JS 包的存放位置。abiFilters 建议只留设备和模拟器需要的架构至少 arm64-v8a如果你要在 x86_64 模拟器上调试也要加 x86_64。4.4 在鸿蒙页面挂载 ReactNativeView在Index.ets里写入口页面通过 RNOH 提供的容器组件挂载 RN 实例。示意代码如下import { ReactNativeView } from react-native-ohos/react-native; Entry Component struct Index { build() { Stack() { ReactNativeView() .surfaceName(RNAppSurface) .width(100%) .height(100%) } } }ReactNativeView 内部会自动完成 RN 运行时初始化、Bundle 加载和组件树渲染。如果你的业务是“鸿蒙原生为主、RN 页面仅占一部分”同样可以把 ReactNativeView 放进一个局部的 Column 里比如只让它占屏幕下半部分。4.5 开发调试Metro 热更新怎么连RN 开发调试的核心是 Metro。先在鸿蒙工程同级目录创建一个 RN 工程或者复用已有 RN 项目然后在项目根目录执行npm start这会启动 Metro默认监听 8081 端口。鸿蒙应用启动后会去加载 JS Bundle。开发模式下这个 Bundle 来自 Metro 服务。难点在于真机访问。真机通过 USB 连电脑时直接用localhost:8081是不行的因为 localhost 指向手机自己。我用得比较多的是 hdc 的端口转发功能hdc fport tcp:8081 tcp:8081把手机上的 8081 端口映射到电脑的 8081这样应用里写的localhost:8081就能访问到电脑上的 Metro。如果不想做转发也可以直接写电脑的局域网 IP比如http://192.168.1.10:8081/index.bundle?platformharmony。还有一个必须同步做的配置在 RN 工程的metro.config.js里把 harmony 平台的后缀加入识别列表const { getDefaultConfig } require(react-native/metro-config); module.exports (async () { const config await getDefaultConfig(__dirname); config.resolver.sourceExts [...config.resolver.sourceExts, harmony.tsx, harmony.ts, harmony.js]; return config; })();加了这个Metro 才能识别Component.harmony.js这类平台专属代码也才能响应platformharmony的打包请求。4.6 生产构建从 JS Bundle 到 HAP 包调试没问题后要出发布包。流程分三步第一步把 JS Bundle 打成独立文件npx react-native bundle \ --platform harmony \ --dev false \ --entry-file index.js \ --bundle-output harmony/entry/src/main/resources/rawfile/index.bundle.harmony.js如果你用的 RNOH 版本不支持--platform harmony也可以用它内置的打包命令代替具体看包的 README。关键是产出一个压缩后的 JS 文件放到鸿蒙工程的 rawfile 目录。第二步在 DevEco Studio 里选择 Build - Build Hap(s)hvigor 会编译原生代码、打包资源生成.hap文件。第三步在真机安装验证。先用 DevEco 的自动签名功能对应用签名然后执行hdc install -r entry-default-signed.hap到这里一个完整的 RN 应用就算真正跑在鸿蒙设备上了。4.7 把鸿蒙能力封装成鸿组件的一个最小思路最后补一个和标题直接相关的部分怎么把鸿蒙原生能力封装成 RN 页面可调用的“鸿组件”。我不给整段代码因为不同 RNOH 版本的模板差异比较大但套路是固定的。先在 TS 侧定义接口声明方法和回调类型。然后在鸿蒙侧写一个原生模块类继承 RNOH 约定的 TurboModule 基类在类里调用鸿蒙的 API 并返回结果。接着把模块注册到模块工厂里RN 启动时会自动收集所有已注册模块。JS 端通过全局模块注册表获取import { TurboModuleRegistry } from react-native; const SampleModule TurboModuleRegistry.get(RnoSample); const version await SampleModule?.getSystemVersion();这个封装思路和安卓/iOS 上的原生模块几乎一样。你只要封装一次RN 应用里的所有页面都能复用。更重要的是后续鸿蒙能力补全得越多RN 页面能用的原生能力就越接近安卓端逐步减少条件分支。5. 常见问题与排错实战5.1 启动白屏九成是这四件事RN 应用启动白屏是我在集成时遇到最多的问题。排查顺序基本固定网络权限没有加。前面强调过ohos.permission.INTERNET漏了Metro Bundle 根本加载不出来。Bundle URL 不对。开发模式下要看表面配置的 packagerUrl 是否正确指向 Metro生产模式下要看 rawfile 里有没有对应文件路径大小写对不对。surfaceName 不匹配。鸿蒙侧 surfaceName 和 RN 注册的 appKey 对不上组件树建不出来。ABI 不匹配。用 x86_64 模拟器跑 arm64-v8a 的 so直接加载失败变白屏。排查时先看 DevEco 的 Log 面板过滤ReactNativeJS和RNOH这两个标签错误都会打在原生日志里。别去猜日志比任何推断都准。5.2 网络请求发不出去先看权限和明文配置RN 页面里的接口请求在鸿蒙上有个很常见的误区代码在安卓上正常在鸿蒙上报错有人以为是 RC 的 fetch 出了问题。其实大部分是鸿蒙网络策略更严格。第一层是权限前面说过。第二层是明文 HTTP 限制。鸿蒙对http://明文流量默认是拦截的如果你开发环境里的接口没有 HTTPS就要在 module.json5 里配置网络安全策略或者在调试阶段临时放开明文传输。很多论坛里问“鸿蒙请求报 2300056”的基本都能归到网络权限和明文策略这两类。5.3 第三方 RN 组件在鸿蒙上没有反应RN 世界里大量组件依赖原生模块比如摄像头、地图、支付。这些组件在鸿蒙端如果没有对应的原生实现调用时就会出现“方法不存在”或直接崩溃。这不是你代码的问题而是该库没有鸿蒙适配。处理方式有三种一是找替代库优先选有人做了鸿蒙分支的二是自己按 TurboModule 规范补一个鸿蒙实现接口对齐原库三是用条件编译让鸿蒙端走另一套实现。最不推荐的方式是硬改 JS 层去模拟原生能力绕来绕去最后性能一塌糊涂。5.4 布局差异和安全区处理虽然 RNOH 复用了 Yoga 布局引擎视觉一致性已经很好但依然有细节差异。比如部分overflow、boxShadow、背景渐变组合的效果在 ArkUI 的渲染路径上支持不完全表现是“差了一点”。应对办法是设计阶段就给鸿蒙一个降级方案把阴影和模糊在鸿蒙端调轻一些。安全区问题更普遍。鸿蒙的挖孔屏、全面屏底部手势区处理方式和安卓不完全一致RN 的 SafeAreaView 在鸿蒙端需要依赖系统安全区 API 的映射。经验之谈不要在页面底部贴太近边缘的可点击元素RN 侧统一用 padding 兜底再按鸿蒙端实际设备微调。5.5 如何高效看日志和抓现场鸿蒙调试离不开 hdc。它和 adb 类似常用命令有hdc list targets hdc shell hdc file send hdc install看 RN 的 JS 日志在 DevEco 的 Log 窗口过滤ReactNativeJS看原生崩溃过滤RNOH和libc。如果崩溃发生在so库加载阶段过滤dlopen或Hsp。我在定位白屏和崩溃问题时基本都是靠这两组日志快速分流。6. 落地经验我踩过的坑与建议6.1 工程师最该问自己的一个问题接手 RN 鸿蒙化项目先别急着写代码。第一件事是盘点依赖整个 RN 工程里用了哪些第三方库哪些用到原生模块这些原生模块有没有鸿蒙实现没有的话是替换、自研还是让鸿蒙端绕过去把这份清单列出来项目周期基本就能估出来。我见过太多团队写代码写一半才发现某个核心原生库在鸿蒙上完全没有适配被迫推翻方案。6.2 性能调优的方向RN 在鸿蒙上的性能瓶颈通常不在渲染而在 JS 执行和通信开销。优先做这几件事开启 Hermes 引擎并启用字节码缓存把大列表页面替换成复用的原生滚动容器对图片网络请求做缓存裁剪首屏 Bundle 尽量瘦身把非首屏模块动态加载。这些手段在安卓上都验证过了鸿蒙端同样生效。6.3 上架鸿蒙市场要注意什么如果应用要发布到鸿蒙应用市场提前准备证书和签名。DevEco 里可以用华为开发者账号自动生成调试证书但发布证书需要在开发者后台申请并且要配置包名、权限声明、隐私说明。鸿蒙商店审核对权限最小化要求很高别一次性把所有权限都申请了用到再申请。6.4 团队协作上还有一个隐藏成本RN 和鸿蒙原生是两个团队的话一定要在项目初期约定好模块接口规范。JS 端不直接引用鸿蒙包的内部 API统一走自己封装的模块层鸿蒙端不直接改 RN 工程里的 JS 代码只通过暴露的能力和事件来协作。这样两端可以并行开发不会互相阻塞。否则一旦 RN 版本升级或者鸿蒙 SDK 更新两边就要一起返工。我个人在实际接入这套方案时最大的体会是RN 在鸿蒙上的技术栈已经够用了真正决定项目成败的反而是工程纪律。先把版本对齐、权限配齐、依赖盘点清楚这三个基本动作做扎实后面大部分坑都可以绕过去。如果你正准备把现有 RN 应用搬上鸿蒙或者正在为难搞的页面集成头疼按这个顺序走一遍应该能帮你省掉我当初花了好几周才摸清的弯路。
返回列表