在跨平台开发日益普及的今天,将 React Native 应用部署到 OpenHarmony 真机是许多前端开发者面临的挑战。本文将以实战为导向,带你一步步完成环境配置、依赖安装、工程集成与调试,同时梳理 React 与鸿蒙 ArkTS 侧的核心知识结构,助你快速上手。

为什么选择 React Native for OpenHarmony

面对 OpenHarmony 这个新兴生态,许多开发者和我一样,最初会感到困惑:不熟悉 DevEco Studio,不熟悉 ArkTS/原生代码。如果一上来就投入大量精力学习 ArkTS,学习曲线会非常陡峭。

因此,我选择从 React Native for OpenHarmony (RNOH) 切入。利用自己熟悉的 React 思维,先把 UI 跑起来,再逐步理解鸿蒙侧的结构与原理。这种方式不仅能降低入门门槛,还能让你在短时间内看到成果,增强信心。

在实际开发中,React 作为成熟的前端框架,其组件化、状态管理、事件交互等概念在 RNOH 中同样适用。这意味着你之前积累的 前端工具UI开发 经验可以平滑迁移。同时,RNOH 也保留了 OpenHarmony 的平台能力,比如通过 ArkTS 管理页面入口和原生容器。

作为刚开始接触 OpenHarmony、DevEco Studio 和 React 的初学者,Day1 给自己的目标是:按“Windows 11 + DevEco Studio + OpenHarmony SDK + React Native for OpenHarmony(RNOH)”这条常见流程,把链路完整跑通一次:创建工程 → 集成 RN → 真机运行 → 用最小 React 示例验证交互

关键环境变量配置

在开始编码之前,必须确保开发环境正确配置。以下是几个关键步骤:

2.1 配置 hdc 环境变量

右键“开始” → “系统” → “高级系统设置” → “环境变量”。在“系统变量”中找到 Path,编辑并添加 hdc 路径。

⚠️ 重要:必须指向 toolchains 目录,而不是父目录。默认路径为:C:\Users\用户名\AppData\Local\Huawei\Sdk\default\openharmony\toolchains

2.2 配置 HDC_SERVER_PORT

  • 变量名: HDC_SERVER_PORT
  • 变量值: 7035

2.3 配置 CAPI 架构

  • 变量名: RNOH_C_API_ARCH
  • 变量值: 1

2.4 清理 npm 缓存(可选)

如果遇到依赖安装问题,可以执行:npm cache clean --force

提示:配置环境变量后,建议重启终端或 DevEco Studio 以确保生效。

鸿蒙依赖安装与配置

环境就绪后,接下来是项目依赖的安装和配置。

3.1 添加 harmony 脚本到 package.json

package.json 中添加以下脚本,以便后续运行鸿蒙相关命令:

{
"scripts": {
"harmony": "react-native bundle-harmony --dev"
}
}

3.2 安装鸿蒙化依赖包

⚠️ 注意版本匹配:必须确保 React Native 版本与 RNOH 版本对应。例如,RN 0.72 对应 @react-native-oh/react-native-harmony@0.72.90

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

执行安装命令:

npm i @react-native-oh/react-native-harmony@0.72.90

如果遇到网络问题,可以尝试使用淘宝镜像:npm config set registry https://registry.npmmirror.com

️ 鸿蒙侧集成 RNOH

依赖安装完成后,需要在鸿蒙工程中集成 RNOH。以下是关键步骤:

4.1 创建目录结构

按照以下结构组织文件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.2 CMakeLists.txt(关键片段)

CMakeLists.txt 用于编译 C++ 代码,确保正确链接 RNOH 库:

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)
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
add_library(rnoh_app SHARED
  "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
  "./PackageProvider.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)

4.3 PackageProvider.cpp(关键片段)

PackageProvider 负责注册原生模块:

#include "RNOH/PackageProvider.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
  return {};
  }

4.4 修改 build-profile.json5

确保包含 x86_64 架构,以支持模拟器:

{
  apiType: "stageMode",
  buildOption: {
    externalNativeOptions: {
      path: "./src/main/cpp/CMakeLists.txt",
      arguments: "",
      cppFlags: "",
      abiFilters: ["arm64-v8a", "x86_64"],
    },
  },
}

4.5 修改 EntryAbility.ets

EntryAbility 是应用入口,需要加载 RN 页面:

import { RNAbility } from '@rnoh/react-native-openharmony';
export default class EntryAbility extends RNAbility {
  protected getPagePath(): string {
    return "pages/Index"
  }
  override onCreate(want: Want): void {
    super.onCreate(want);
    // 原有初始化代码...
  }
}

4.6 创建 RNPackagesFactory.ets

RNPackagesFactory 用于创建原生包实例:

import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [];
}

4.7 Index.ets(关键配置)

Index.ets 是页面的核心配置:

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 {
    // Ark 侧默认 back 会终止/后台,交给 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,
            enableBackgroundExecutor: false,
            enableCAPIArchitecture: true,
            arkTsComponentNames: []
          },
          initialProps: { "foo": "bar" } as Record,
          appKey: "Test0121",
          wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
          onSetUp: (rnInstance) => {
            rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
          },
          jsBundleProvider: new TraceJSBundleProviderDecorator(
            new AnyJSBundleProvider([
              new MetroJSBundleProvider(),
              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%')
  }
}

⚠️ 特别注意:

  • appKey 必须与 React Native 工程中 AppRegistry.registerComponent 注册的名称完全一致,否则会导致白屏。
  • enableNDKTextMeasuringenableCAPIArchitecture 必须设置为 true,否则应用无法正常启动。
  • 每次修改 CPP 侧代码后,必须点击 DevEco Studio 右上角的 Sync Now 同步工程。
[AFFILIATE_SLOT_1]

运行结果与常见问题排查

完成上述配置后,就可以在鸿蒙真机上看到 React Native 页面了。以下是一些常见问题及解决方法:

运行结果截图

以下是成功运行的截图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常见问题排查

现象可能原因解决方法
白屏appKey 不一致对照 AppRegistry.registerComponent('xxx') 的名字,保持两边完全一致
修改 C++ 后无变化未同步工程点击 Sync Now 再构建
找不到 bundle / Metro 不生效jsBundleProvider 选择了 File/Resource,但文件不存在先用 MetroJSBundleProvider() 跑通,再逐步切到 file/resource

如果遇到白屏,首先检查 appName 是否一致;其次检查 Metro 是否正常启动。建议使用 AngularReact 的调试工具辅助排查。

️ 环境清单与总结

以下是本文使用的环境版本,供你参考:

  • Windows: Windows 11
  • DevEco Studio: 具体版本请查看官方文档
  • OpenHarmony SDK: 对应版本
  • React Native: 0.72
  • RNOH: @react-native-oh/react-native-harmony@0.72.90

欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net

[AFFILIATE_SLOT_2]

总结一下,本文带你完成了从环境配置到真机运行的完整流程。核心要点包括:

  • ✅ 配置 hdc、HDC_SERVER_PORT、CAPI 等环境变量
  • ✅ 安装鸿蒙化依赖并匹配版本
  • ✅ 在鸿蒙工程中集成 RNOH,包括 CMakeLists、PackageProvider、EntryAbility 等
  • ✅ 掌握常见问题的排查方法

通过这套流程,你可以快速将 React 应用部署到 OpenHarmony 设备,开启跨平台开发的新篇章。未来,你还可以深入探索 ArkTS 的原生能力,将 React 与鸿蒙生态深度结合。