【开源鸿蒙跨平台开发学习笔记】Day01:React Native 开发 HarmonyOS-环境搭建篇
React Native 开发 HarmonyOS
前言
本次参加开源鸿蒙跨平台开发学习活动,选择了 React Native 开发 HarmonyOS技术栈,在学习的同时顺便整理成一份系列笔记,记录从环境到开发的全过程。react-native-openharmony 目前已经比较成熟,能够让 RN 项目直接运行在鸿蒙设备或模拟器上,对于想做跨平台开发的人来说,是一个不错的选择。
本篇作为第一篇,重点介绍环境准备与项目初始化流程。
一、为什么选择 React Native 开发 HarmonyOS?
React Native 的优势在于生态成熟、组件丰富、工具链完善,开发效率高。通过社区的适配层,RN 可以在 Android、iOS、HarmonyOS 三端同时运行,这意味着:
-
一套代码多端复用;
-
保留现有 RN 技术栈;
-
降低适配鸿蒙的门槛;
-
更适合前端或跨端背景的开发者。
就比如我本身就是前端开发者,有Vue和RN开发经验,如果要开发HarmonyOS应用,使用RN的跨平台开发就是一个不错的选择。
RNOH架构

如图,React Native for OpenHarmony 在 React Native 的新架构(0.68 以及之后的版本)的基础上,进行了鸿蒙化的适配。按照功能可以进行如下的划分:
- RN 应用代码:开发者实现的业务代码。
- RN 库代码:在 React Native 供开发者使用的组件和 API 的封装与声明。
- JSI(JavaScript Interface):JavaScript 与 CPP 之间进行通信的 API。
- React Common:所有平台通用的 CPP 代码,用于对 RN 侧传过来的数据进行预处理。
- OpenHarmony 适配代码:接收并处理 React Common 传过来的数据,对接原生的代码,调用 ArkUI 的原生组件与 API。主要包括了两个部分:分别是 TurboModule 与 Fabric。
- OS 代码:对接系统底层功能,根据适配层代码传过来的数据进行渲染,或完成对应的功能。
二、环境准备
正式开始前,需要准备以下工具:
| 环境 | 说明 |
|---|---|
| Node.js 18+ | 建议使用 nvm 管理 |
| React Native CLI | 用于创建 RN 项目 |
| OpenHarmony SDK(必装) | 可通过 DevEco Studio 或 CLI 安装 |
| HarmonyOS 模拟器或真机 | 真机可直接 USB 调试 |
| react-native-openharmony | RN 的鸿蒙适配层 |
版本兼容性确认
-
React Native ≥ 0.71
-
react-native-openharmony ≥ 根据OpenHarmony SDK对应
-
DevEco Studio ≥ 5.1(可选)
-
OpenHarmony SDK ≥ API 12(推荐)
HDC环境变量设置
hdc 是 OpenHarmony 为开发人员提供的用于调试的命令行工具,鸿蒙 React Native 工程使用 hdc 进行真机调试。hdc 工具通过 OpenHarmony SDK 获取,存放于 SDK 的 toolchains 目录下,请将 {DevEco Studio安装路径}/sdk/{SDK版本}/openharmony/toolchains 的完整路径添加到环境变量中。
- windows 环境变量设置方法:
- 在此电脑 > 属性 > 高级系统设置 > 高级 > 环境变量中,编辑系统变量path,添加hdc工具路径。
- 在此电脑 > 属性 > 高级系统设置 > 高级 > 环境变量中,添加 HDC 端口变量名为:
HDC_SERVER_PORT,变量值可设置为任意未被占用的端口,如7035。

- macOS 环境变量设置方法:
- 打开终端,执行以下命令,打开
.bash_profile文件。vi ~/.bash_profile - 输入以下内容,在 PATH 路径下添加 HDC 工具路径和添加
HDC_SERVER_PORT端口信息:export PATH="/Applications/DevEco-Studio.app/Contents/sdk/{版本路径}/openharmony/toolchains:$PATH" # 按照实际 SDK 安装路径配置,需要选择{显示包内容} HDC_SERVER_PORT=7035 launchctl setenv HDC_SERVER_PORT $HDC_SERVER_PORT export HDC_SERVER_PORT -
执行以下命令使配置的环境变量生效:
source ~/.bash_profile
-
配置 CAPI 版本环境变量:当前RN框架提供的 Demo 工程默认为 CAPI 版本,需要配置环境变量
RNOH_C_API_ARCH = 1。
-
Windows 环境:在此电脑 > 属性 > 高级系统设置 > 高级 > 环境变量中,在系统变量中点击新建,添加变量名为:
RNOH_C_API_ARCH,变量值为1。 -
Mac环境:在终端中,输入以下命令来设置环境变量:export RNOH_C_API_ARCH=1,确认环境变量是否成功设置。在终端中输入以下命令:echo $RNOH_C_API_ARCH。如果输出为 1,则表示环境变量已成功设置。如果希望在每次打开终端时都自动设置该环境变量,可以将上述 export 命令添加到你的 bash 配置文件(例如~/.bash_profile、~/.bashrc 或 ~/.zshrc)。编辑相应的文件,并在末尾添加以下行:export RNOH_C_API_ARCH=1,保存文件并关闭编辑器。
-
为了使用加速
npm包的下载,可以配置镜像源strict-ssl=false sslVerify=false registry=https://repo.huaweicloud.com/repository/npm/修改 registry 后需执行
npm cache clean --force清理缓存,以确保新的 registry 生效。完成以上环境配置即可进行鸿蒙的 React Native 项目开发。
三、安装 OpenHarmony SDK
DevEco Studio(官方推荐,最稳定)
-
下载 DevEco Studio 5.1+
-
第一次启动会弹出“安装 OpenHarmony SDK”提示
-
按向导安装完整 SDK
-
安装完成后可创建模拟器
验证方式:
DevEco → Tools → SDK Manager → 能看到 HarmonyOS API 12/13,即安装成功。
四、创建React Native工程
可选择一个目录,使用 React Native 内置的命令行工具来创建一个名为 “HarmonyRNTest” 的新项目。这个命令行工具不需要安装,可以直接用 node 自带的 npx 命令来创建,目前 React Native for OpenHarmony 仅支持 0.72.5 版本的 React Native:
npx react-native@0.72.5 init HarmonyRNTest --version 0.72.5
cd HarmonyRNTest

五、下载并安装鸿蒙化依赖
使用VS Code IDE打开生成的模版代码。

打开目录下的package.json,在scripts下新增 OpenHarmony 的执行脚本:
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest",
"dev": "react-native bundle-harmony --dev"
}
运行安装依赖包命令:推荐使用pnpm我用npm总是失败。
pnpm i @react-native-oh/react-native-harmony@特定版本
注意:一定要使用和你的HarmonyOS SDK版本对应的react-native-harmony版本,否者会报错!!!
如何查找对应自己环境的版本:
- 查看自己配置之前环境变量配置的toolchains对应的SDK版本号:

我的是6..0.0.47;
-
然后去https://gitcode.com/openharmony-sig/ohos_react_native/blob/master/docs/zh-cn/release-notes/react-native-harmony-v6.0.0.508.md地址查询对应的版本号:

根据我的SDK版本6.0.0.47所以我下载的react-native-harmony版本就应该是0.72.90;
pnpm i @react-native-oh/react-native-harmony@0.72.90
再去修改metro.config.js文件添加OpenHarmony 的适配代码。
const {mergeConfig, getDefaultConfig} = require('@react-native/metro-config');
const {createHarmonyMetroConfig} = require('@react-native-oh/react-native-harmony/metro.config');
/**
* @type {import("metro-config").ConfigT}
*/
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), createHarmonyMetroConfig({
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
}), config);
运行生成 bundle 文件的命令。运行成功后,会在 HarmonyRNTest/harmony/entry/src/main/resources/rawfile 目录下生成 bundle.harmony.js 和 assets 文件夹,assets 用来存放图片(如果 bundle 中不涉及本地图片,则没有 assets 文件夹)。
pnpm run dev

执行成功后,在项目根目录会生成Harmony文件夹。

好了,看到以上文件夹RN这边先告一段落。
六、在HarmonyOS工程中集成React Native
首先我们创建一个空的新工程。


然后我们再执行如下命令,在原生工程中集成 RNOH:
ohpm i @rnoh/react-native-openharmony@0.72.90
集成之后可以查看工程根目录的oh-package.json5文件:

好接下来修改默认工程入口Ability的实现:
- 需要重写getPagePath方法,返回程序的入口page;
- 如果需要扩展使用对应的生命周期函数请在代码中先用super调用父类也就是RNAbility的生命周期函数实现,因为RNAbility在生命周期函数中进行了对应的操作,这样才能确保原有功能不丢失;
- 需确保函数的参数列表与父类保持兼容,建议添加override关键字,以提升代码可读性并增强编译器检查;
import { RNAbility } from '@rnoh/react-native-openharmony';
import { Want } from '@kit.AbilityKit';
export default class EntryAbility extends RNAbility {
getPagePath(): string {
return "pages/Index"
}
override onCreate(want: Want): void {
super.onCreate(want);
}
}
在entry\src\main\ets目录下新增RNPackagesFactory.ets文件,它用来在 @rnoh/react-native-openharmony 库中导入 RNPackageContext 和 RNPackage以及在文件中导出 createRNPackages 方法,用于创建三方库或自定义 TurboModule、Fabric的package 对象,由于这次不涉及三方库,这块直接返回[]:
import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [];
}
打开Index.ets文件,添加RNOH的使用代码,修改后如下:
import {
AnyJSBundleProvider,
ComponentBuilderContext,
FileJSBundleProvider,
MetroJSBundleProvider,
ResourceJSBundleProvider,
RNApp,
RNOHErrorDialog,
RNOHLogger,
TraceJSBundleProviderDecorator,
RNOHCoreContext
} from '@rnoh/react-native-openharmony';
import { createRNPackages } from '../RNPackagesFactory';
@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {}
const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent)
@Entry
@Component
struct Index {
@StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined
@State shouldShow: boolean = false
private logger!: RNOHLogger
aboutToAppear() {
this.logger = this.rnohCoreContext!.logger.clone("Index")
const stopTracing = this.logger.clone("aboutToAppear").startTracing();
this.shouldShow = true
stopTracing();
}
onBackPress(): boolean | undefined {
// NOTE: this is required since `Ability`'s `onBackPressed` function always
// terminates or puts the app in the background, but we want Ark to ignore it completely
// when handled by RN
this.rnohCoreContext!.dispatchBackPress()
return true
}
build() {
Column() {
if (this.rnohCoreContext && this.shouldShow) {
if (this.rnohCoreContext?.isDebugModeEnabled) {
RNOHErrorDialog({ ctx: this.rnohCoreContext })
}
RNApp({
rnInstanceConfig: {
createRNPackages,
enableNDKTextMeasuring: true, // 该项必须为true,用于开启NDK文本测算
enableBackgroundExecutor: false,
enableCAPIArchitecture: true, // 该项必须为true,用于开启CAPI
arkTsComponentNames: []
},
initialProps: { "foo": "bar" } as Record<string, string>,
appKey: "HarmonyRNTest",
wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
onSetUp: (rnInstance) => {
rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
},
jsBundleProvider: new TraceJSBundleProviderDecorator(
new AnyJSBundleProvider([
new MetroJSBundleProvider(),
// NOTE: to load the bundle from file, place it in
// `/data/app/el2/100/base/com.rnoh.tester/files/bundle.harmony.js`
// on your device. The path mismatch is due to app sandboxing on OpenHarmony
new FileJSBundleProvider('/data/storage/el2/base/files/bundle.harmony.js'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
]),
this.rnohCoreContext.logger),
})
}
}
.height('100%')
.width('100%')
}
}
注意:RNApp的参数appKey需要与RN工程中AppRegistry.registerComponent注册的appName保持一致,否则会导致白屏。


通过以上方式可以获取到这个注册的名字,也就是RN工程的名字。
然后我们还需要补充 CPP 侧代码,在entry/src/main目录下新建cpp文件夹。在cpp目录下新增CMakeLists.txt,并将RNOH的适配层代码添加到编译构建中生成librnoh_app.so。
CMakeLists.txt文件内容如下:
project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(RNOH_CPP_DIR "${OH_MODULE_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
add_compile_definitions(WITH_HITRACE_SYSTRACE)
set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
add_library(rnoh_app SHARED
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
在cpp目录下新增PackageProvider.cpp,该文件导入RNOH/PackageProvider,并且实现getPackages方法,用于创建三方库或自定义TurboModule或Fabric的package对象。此处不涉及三方库与自定义TurboModule或组件,需要返回空数组。然后打开entry/build-profile.json5,将cpp中的代码添加到应用工程的编译构建任务中;如果x86_64架构的模拟器上运行应用,需在externalNativeOptions配置中额外添加abiFilters字段,并包含x86_64架构参数,如下所示,abiFilters字段当前被注释,默认仅构建适应于arm64-v8a架构的版本。
加载bundle包
在上一章节中已经完成了 bundle 文件的生成,接下来将它加载到 DevEco Studio 中以运行 MyApplication 项目。我们采用加载 本地bundle 包的形式,将 bundle 文件和 assets 图片放在 entry/src/main/resources/rawfile 路径下,在 entry/src/main/ets/pages/Index.ets 中使用。
将之前我们执行npm run dev后的harmony/entry/src/main/resources/rawfile目录下的bundle文件和assets图片拷贝到鸿蒙工程entry/src/main/resources/rawfile路径下。


启动并运行工程~
然后就遇到了一个比较坑的问题,依赖:@rnoh/react-native-openharmony在CMakeLists.txt配置的${OH_MODULE_DIR}/@rnoh/react-native-openharmony路径下是一个“替身”文件,这个可能是MacOS才会报的错。
更多推荐




所有评论(0)