一文搞定开源鸿蒙React Native项目配置Metro热加载

新星杯·14天创作挑战营·第17期 10w+人浏览 660人参与

创建React Native工程

在指定目录下执行以下命令,创建react native工程。

image-20251127160924720

RnohGuide工程添加鸿蒙化配置。

  • package.json配置文件scripts属性中添加编译开源鸿蒙命令:
{
  // ...
  "scripts": {
    // ...
    "harmony": "react-native bundle-harmony --dev"
  },
  // ...
}

image-20251127162623299

  • RnohGuide工程根目录执行npm i @react-native-oh/react-native-harmony@x.x.x命令按照开源鸿蒙依赖,此处@x.x.x为安装的指定版本号,不指定则默认安装最新版本,可以参考React Native鸿蒙化版本信息选择合适的版本。

image-20251127172602491

配置Metro热加载

React Native使用Metro构建JavaScript代码和资源,要启用Metro热加载能力,需要在项目的metro.config.js配置文件中配置开源鸿蒙的Metro配置选项。除了构建和运行React Native应用所需基本配置的扩展@react-native/metro-config@expo/metro-config,若使开源鸿蒙也支持该能力,则需要引入扩展react-native-harmony,通过createHarmonyMetroConfig创建开源鸿蒙的Metro。在RnohGuide工程根目录metro.config.js配置文件中按照以下代码完成初级配置。

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const { createHarmonyMetroConfig } = require('@react-native-oh/react-native-harmony/metro.config');
/**
 * Metro configuration
 * https://facebook.github.io/metro/docs/configuration
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {
    transformer: {
        getTransformOptions: async () => ({
            transform: {
                experimentalImportSupport: false,
                inlineRequires: true,
            },
        }),
    }
};

module.exports = mergeConfig(
    getDefaultConfig(__dirname), 
    createHarmonyMetroConfig({
        reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
    }),
    config
);

image-20251127174044184

创建和配置开源鸿蒙工程

注意:在创建工程时指定工程存储目录为React Native工程RnohGuide根目录下的harmony目录下,如下图所示。

image-20251127174958767

添加React Native配置

  • entry目录下执行命令ohpm i @rnoh/react-native-openharmony@0.72.90

image-20251127175930484

补充cpp侧代码

  • entry/src/main目录下新建cpp文件。
  • cpp目录下新增CMakeLists.txt,并将rnoh的适配层代码添加到编译构建冲生成librnoh_app.so
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(harmony)

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方法,用于创建三方库或自定义TurboModuleFabricpackage对象。
# include "RNOH/PackageProvider.h"
#include <vector>

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {};
}
  • 打开entry/build-profile.json5配置文件,将cpp中的代码添加到应用工程的编译构建任务中;如果x86_64架构的模拟器上运行应用,需在externalNativeOptions配置中额外添加abiFilters字段,并包含x86_64架构参数,如下所示,abiFilters字段当前被注释,默认仅构建适应于arm64-v8a架构的版本。
{
  // ...
  "buildOption": {
    // ...
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "arguments": "",
      "cppFlags": "",
    },
    "externalNativeOptions": {
      "abiFilters": [
        "arm64-v8a",
        "x86_64"
      ]
    }
  },
  // ...
}

补充ArkTS侧代码

  • 修改entry/src/main/ets/entryability/EntryAbility.ets继承类为RNAbility,并重写getPagePath方法,返回程序入口page路径。
export default class EntryAbility extends RNAbility {
  
  protected getPagePath(): string {
    return "pages/Index"
  }
  
  onCreate(want: Want): void {
    super.onCreate(want);
    try {
      this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    } catch (err) {
      hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
    }
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
  }
  // ...
}
  • entry/src/main/ets目录下新增RNPackagesFactory.ets,需要导入rnoh/react-native-openharmony包中的RNOHPackageRNPackageContext,以及导出用于创建三方库或自定义TurboModuleFabric的package对象方法createRNPackages
import { RNOHPackage, RNPackageContext } from '@rnoh/react-native-openharmony';

export function createRNPackages(ctx: RNPackageContext): RNOHPackage[] {
  return [];
}
  • entry/src/main/ets/pages/Index.ets文件代码,添加RNOH的使用代码。其中RNAPP的参数appKey需要与React Native工程中AppRegistry.registerComponent注册的appName保持一致,否则会导致白屏。
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: "RnohGuide",
          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%')
  }
}

使用Metro

如果使用的是RNSurface启动的RN框架(在entry/src/main/pages/Index.ets代码中build函数中查看),需要创建一个RNComponentContext,并在contextdevToolsController中增加如下的事件监听,并删除原有的RNInstance实例,重新启动一个新的RNInstance实例,即可启用热加载的功能。

this.ctx.devToolsController.eventEmitter.subscribe("RELOAD", async () => {
this.cleanUp();
ths.init();
})

RNOH提供了MetroJSBundleProviderAPI用于加载Metro服务提供的jsBundle,在RNApp配置中开启Metro服务。

RNApp({
  // ...
  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),
})

要想使Mator服务正常启用,需要先在RnohGuide项目根目录下执行npm run harmony命令,在harmony/entry/src/main/resources/rawfile目录下生成bundle.harmony.jsassets文件夹(如果bundle中不涉及本地图片,则没有assets文件夹)。

image-20251128115153731

harmony工程需要的资源编译完成后,先执行命令hdc rport tcp:8081 tcp:8081,使用DevEco Studio或者hdc ...命令将RnohGuide安装到模拟器或真机设备上。

image-20251128150149668

回到RnohGuide根目录,修改App.tsx内容如下所示:

image-20251128150439826

RnohGuide根目录执行npm run start,选择r启动热更新(后续更改均可在终端输入r推送修改内容,直到你手动停止启动的Metro)。

image-20251128151537542

image-20251128151620010

至此,已经完成Metro热加载能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值