React Native 三方库 react-native-version-number 鸿蒙适配实战:从零到版本信息展示
是一个轻量级 React Native 库,用于获取应用的版本号、构建号和包名标识。在 iOS 和 Android 上,它通过读取原生配置(Info.plist)获取版本信息。鸿蒙适配版本由CPF-RN团队维护,使用鸿蒙原生的API 读取应用包信息。将集成到 HarmonyOS NEXT 平台的核心流程可概括为“源码复制 + 手动桥接”JS 端:安装,Metro 自动重定向 importArkTS
React Native 三方库 react-native-version-number 鸿蒙适配实战:从零到版本信息展示
本文记录了将开源 React Native 三方库
react-native-version-number安装集成到 HarmonyOS NEXT 平台的完整过程,涵盖 npm 包安装、Metro 重定向配置、OHOS 原生源码集成、Codegen 桥接代码手动生成、C++ 原生层配置,以及踩坑排障的完整复盘。
一、背景
1.1 三方库简介
react-native-version-number 是一个轻量级 React Native 库,用于获取应用的版本号、构建号和包名标识。在 iOS 和 Android 上,它通过读取原生配置(Info.plist / BuildConfig)获取版本信息。
鸿蒙适配版本由 CPF-RN 团队维护,使用鸿蒙原生 @kit.AbilityKit 的 bundleManager API 读取应用包信息。
1.2 适配目标
| 目标项 | 说明 |
|---|---|
| 三方库 | react-native-version-number → @react-native-ohos/react-native-version-number |
| RN 框架版本 | @rnoh/react-native-openharmony 0.82.30 |
| React Native 版本 | 0.82.1 |
| 目标平台 | HarmonyOS NEXT (API 12+) |
| 目标能力 | 获取 appVersion、buildVersion、bundleIdentifier |
1.3 版本兼容性
| 鸿蒙适配版本 | 原库版本 | 兼容 RN 版本 | 支持 Autolink | 最低 API | npm 包版本 |
|---|---|---|---|---|---|
| ~0.5.0 | ~0.5.0 | 0.82.* | ✅ 是 | API12+ | npm |
| ~0.4.0 | ~0.4.0 | 0.77.* | ❌ 否 | API12+ | npm |
| ~0.3.7 | ~0.3.7 | 0.72.* | ✅ 是 | API12+ | npm |
| <=0.3.6-0.0.1 | ≤0.3.6 | 0.72.* | ❌ 否 | API12+ | npm |
⚠️ 版本注意:本文使用
0.5.0-beta.1,对应 RN 0.82.x。如果你使用其他 RN 版本,请选择对应的鸿蒙适配版本。
1.4 支持能力一览
| API | 功能 | 鸿蒙支持 |
|---|---|---|
appVersion |
应用版本号 | ✅ |
buildVersion |
构建版本号 | ✅ |
bundleIdentifier |
包名标识 | ✅ |
二、适配路线图
整个适配过程分为以下阶段:
| 阶段 | 内容 | 关键产出 |
|---|---|---|
| 阶段一 | JS 端依赖安装 | package.json 新增依赖 |
| 阶段二 | Metro 重定向验证 | bundle.harmony.js 中 import 自动替换 |
| 阶段三 | OHOS 端源码集成 | version_number/ 目录 3 个 ArkTS 文件 |
| 阶段四 | Codegen 桥接代码手动生成 | generated/ 目录 3 个新文件 |
| 阶段五 | C++ 原生层配置 | CMakeLists.txt + RNOHGeneratedPackage.h |
| 阶段六 | App 业务代码与 Bundle 构建 | App.tsx + bundle.harmony.js |
| 阶段七 | 构建部署与验证 | 设备端版本信息展示 |
三、逐步适配过程
阶段一:JS 端依赖安装
1.1 安装鸿蒙适配版本
在 RN 项目根目录下安装:
cd AwesomeProject
# 安装对应 RN 0.82.x 的鸿蒙适配版本
npm install @react-native-ohos/react-native-version-number@0.5.0-beta.1

安装完成后,package.json 中会新增:
{
"dependencies": {
"@react-native-ohos/react-native-version-number": "^0.5.0-beta.1"
}
}
1.2 验证安装
cat node_modules/@react-native-ohos/react-native-version-number/package.json | python3 -c "import sys,json; d=json.load(sys.stdin); print('version:', d.get('version'))"
输出:
version: 0.5.0-beta.1
1.3 确认 harmony 目录结构
安装后的 npm 包自带 OHOS 原生源码:
ls -la node_modules/@react-native-ohos/react-native-version-number/harmony/
输出:
drwxr-xr-x 4 rnoh_version_number
-rw-r--r-- 1 rnoh_version_number.har
其中 rnoh_version_number.har 是预编译包,rnoh_version_number/ 是源码目录。我们采用源码直接集成方式。
1.4 查看 JS 端入口文件
// node_modules/@react-native-ohos/react-native-version-number/index.js
import { NativeModules, TurboModuleRegistry } from 'react-native';
const { RNVersionNumber } = TurboModuleRegistry
? TurboModuleRegistry.get('RNVersionNumber').getConstants()
: NativeModules;
type VersionObject = {
appVersion: ?string,
buildVersion: ?string,
bundleIdentifier: ?string
};
const VersionNumber: VersionObject = {
appVersion: RNVersionNumber && RNVersionNumber.appVersion,
buildVersion: RNVersionNumber && RNVersionNumber.buildVersion,
bundleIdentifier: RNVersionNumber && RNVersionNumber.bundleIdentifier
};
export default VersionNumber;
💡 JS 端通过
TurboModuleRegistry.get('RNVersionNumber')获取 TurboModule,然后调用getConstants()获取版本信息常量。模块名RNVersionNumber是三端桥接的关键标识。
阶段二:Metro 重定向验证
2.1 Metro 配置
确保 AwesomeProject/metro.config.js 已配置 Harmony Metro 插件:
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);
2.2 构建 Bundle 并验证重定向
cd AwesomeProject
npx react-native bundle-harmony --dev false
输出中可以看到重定向信息:
[INFO] Redirected imports to 2 harmony-specific third-party package(s):
[INFO] • react-native-tts → @react-native-ohos/react-native-tts
[INFO] • react-native-version-number → @react-native-ohos/react-native-version-number

这说明 import VersionNumber from 'react-native-version-number' 会被自动重定向到鸿蒙适配版本 @react-native-ohos/react-native-version-number。
阶段三:OHOS 端源码集成
在 Myrndemo/entry/src/main/ets/ 下创建 version_number/ 目录,放置 3 个 ArkTS 文件。
3.1 目录结构
entry/src/main/ets/
├── version_number/
│ ├── RNVersionNumberPackage.ets # RN Package 注册入口
│ ├── RNVersionNumberTurboModule.ts # TurboModule 实现(桥接层)
│ └── Logger.ts # 日志工具
├── tts/ # (已有)TTS 相关文件
├── generated/ # Codegen 生成(阶段四产出)
│ ├── ts.ts
│ ├── index.ets
│ ├── turboModules/
│ │ ├── TTSNativeModule.ts
│ │ ├── RNVersionNumber.ts # 新增
│ │ └── ts.ts
│ └── components/
│ └── ts.ts
├── RNPackagesFactory.ets
└── pages/Index.ets
3.2 从 npm 包复制源码
# 创建目标目录
mkdir -p Myrndemo/entry/src/main/ets/version_number
# 复制 3 个源文件
cp AwesomeProject/node_modules/@react-native-ohos/react-native-version-number/harmony/rnoh_version_number/src/main/ets/RNVersionNumberPackage.ets \
Myrndemo/entry/src/main/ets/version_number/
cp AwesomeProject/node_modules/@react-native-ohos/react-native-version-number/harmony/rnoh_version_number/src/main/ets/RNVersionNumberTurboModule.ts \
Myrndemo/entry/src/main/ets/version_number/
cp AwesomeProject/node_modules/@react-native-ohos/react-native-version-number/harmony/rnoh_version_number/src/main/ets/Logger.ts \
Myrndemo/entry/src/main/ets/version_number/
3.3 修改 import 路径
npm 包中的源码 import 路径是 ./generated/ts(相对于库内部),需要修改为 ../generated/ts(相对于项目结构):
RNVersionNumberTurboModule.ts 修改:
- import { TM } from "./generated/ts";
+ import { TM } from '../generated/ts';
RNVersionNumberPackage.ets 修改:
- import { TM } from "./generated/ts";
+ import { TM } from '../generated/ts';
3.4 RNVersionNumberPackage.ets — Package 注册入口
/*
* Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
* Use of this source code is governed by a MIT license that can be
* found in the LICENSE file.
*/
import { RNOHPackage, TurboModulesFactory } from '@rnoh/react-native-openharmony';
import type { TurboModule, TurboModuleContext, } from '@rnoh/react-native-openharmony';
import { TM } from '../generated/ts';
import { RNVersionNumberTurboModule } from './RNVersionNumberTurboModule';
class RNVersionNumberFactory extends TurboModulesFactory {
createTurboModule(name: string): TurboModule | null {
if (name === 'RNVersionNumber' || name === TM.RNVersionNumber.NAME) {
return new RNVersionNumberTurboModule(this.ctx);
}
return null;
}
hasTurboModule(name: string): boolean {
return name === 'RNVersionNumber' || name === TM.RNVersionNumber.NAME;
}
}
export class RNVersionNumberPackage extends RNOHPackage {
createTurboModulesFactory(ctx: TurboModuleContext): TurboModulesFactory {
return new RNVersionNumberFactory(ctx);
}
}
💡 注意这里用的是
RNOHPackage(0.82.x 版本),而不是旧版的RNPackage。
3.5 RNVersionNumberTurboModule.ts — TurboModule 实现
/*
* Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
* Use of this source code is governed by a MIT license that can be
* found in the LICENSE file.
*/
import { bundleManager } from '@kit.AbilityKit';
import { BusinessError } from '@ohos.base';
import { TurboModule } from '@rnoh/react-native-openharmony/ts';
import { TM } from '../generated/ts';
import Logger from './Logger';
const TAG = 'RNVersionNumber';
interface VersionInfo {
appVersion: string,
buildVersion: string,
bundleIdentifier: string
}
export class RNVersionNumberTurboModule extends TurboModule implements TM.RNVersionNumber.Spec {
constructor(ctx) {
super(ctx);
}
getConstants(): Object {
let RNVersionNumber: VersionInfo = {
appVersion: '',
buildVersion: '',
bundleIdentifier: ''
};
try {
const bundleInfos = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT);
RNVersionNumber.bundleIdentifier = bundleInfos.name;
RNVersionNumber.appVersion = bundleInfos.versionName;
RNVersionNumber.buildVersion = String(bundleInfos.versionCode);
Logger.info(TAG, JSON.stringify(bundleInfos));
} catch (err) {
const message = (err as BusinessError).message;
Logger.error(TAG, JSON.stringify(message))
}
return { RNVersionNumber }
}
}
💡 关键实现:
- 使用
bundleManager.getBundleInfoForSelfSync()同步获取当前应用包信息bundleInfos.name→bundleIdentifier(包名,如com.nutpi.rndemo)bundleInfos.versionName→appVersion(语义版本号,如1.0.0)bundleInfos.versionCode→buildVersion(整型构建号,转为字符串,如1000000)getConstants()是同步方法,返回的对象会作为 TurboModule 的常量暴露给 JS 端
3.6 Logger.ts — 日志工具
/*
* Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
* Use of this source code is governed by a MIT license that can be
* found in the LICENSE file.
*/
import hilog from '@ohos.hilog';
class Logger {
private domain: number;
private prefix: string;
private format: string = '%{public}s, %{public}s';
private isDebug: boolean;
constructor(prefix: string = 'MyApp', domain: number = 0xFF00, isDebug = false) {
this.prefix = prefix;
this.domain = domain;
this.isDebug = isDebug;
}
debug(...args: string[]): void {
if (this.isDebug) {
hilog.debug(this.domain, this.prefix, this.format, args);
}
}
info(...args: string[]): void {
hilog.info(this.domain, this.prefix, this.format, args);
}
warn(...args: string[]): void {
hilog.warn(this.domain, this.prefix, this.format, args);
}
error(...args: string[]): void {
hilog.error(this.domain, this.prefix, this.format, args);
}
}
export default new Logger('RNVersionNumber', 0xFF00, false)
阶段四:Codegen 桥接代码手动生成
4.1 为什么需要手动生成?
执行 Codegen 命令后,只生成了 TTS 的桥接代码,没有生成 RNVersionNumber 的:
cd AwesomeProject
RNOH_C_API_ARCH=1 npx react-native codegen-harmony \
--ets-output-path ../Myrndemo/entry/src/main/ets/generated \
--cpp-output-path ../Myrndemo/entry/src/main/cpp/generated \
--project-root-path . \
--no-safety-check
输出:
• ../Myrndemo/entry/src/main/cpp/generated/RNOHGeneratedPackage.h
• ../Myrndemo/entry/src/main/cpp/generated/TTSNativeModule.cpp
• ../Myrndemo/entry/src/main/cpp/generated/TTSNativeModule.h
• ../Myrndemo/entry/src/main/ets/generated/components/ts.ts
• ../Myrndemo/entry/src/main/ets/generated/index.ets
• ../Myrndemo/entry/src/main/ets/generated/ts.ts
• ../Myrndemo/entry/src/main/ets/generated/turboModules/TTSNativeModule.ts
• ../Myrndemo/entry/src/main/ets/generated/turboModules/ts.ts
info Generated 8 file(s)
原因:@react-native-ohos/react-native-version-number 的 NativeRNVersionNumber.ts 没有使用 @codegenConfig 注解,Codegen 扫描不到它。需要手动创建桥接文件。
4.2 创建 ETS 桥接类型文件
generated/turboModules/RNVersionNumber.ts:
/**
* This code was generated by "react-native codegen-harmony"
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*/
import { Tag } from "@rnoh/react-native-openharmony/ts"
export namespace RNVersionNumber {
export const NAME = 'RNVersionNumber' as const
export interface Spec {
getConstants(): {};
}
}
💡
NAME常量值为'RNVersionNumber',与 JS 端TurboModuleRegistry.get('RNVersionNumber')的模块名一致。
4.3 更新 ETS 导出入口
generated/turboModules/ts.ts — 新增 RNVersionNumber 导出:
/**
* This code was generated by "react-native codegen-harmony"
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*/
export * from "./TTSNativeModule"
export * from "./RNVersionNumber"
4.4 创建 C++ 桥接文件
generated/RNVersionNumber.h:
/**
* This code was generated by "react-native codegen-harmony"
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*/
#pragma once
#include "RNOH/ArkTSTurboModule.h"
namespace rnoh {
class JSI_EXPORT RNVersionNumber : public ArkTSTurboModule {
public:
RNVersionNumber(const ArkTSTurboModule::Context ctx, const std::string name);
};
} // namespace rnoh
generated/RNVersionNumber.cpp:
/**
* This code was generated by "react-native codegen-harmony"
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*/
#include "RNVersionNumber.h"
namespace rnoh {
using namespace facebook;
RNVersionNumber::RNVersionNumber(const ArkTSTurboModule::Context ctx, const std::string name) : ArkTSTurboModule(ctx, name) {
methodMap_ = {
ARK_METHOD_METADATA(getConstants, 0),
};
}
} // namespace rnoh
💡
ARK_METHOD_METADATA(getConstants, 0)表示getConstants是同步方法,参数个数为 0。注意这里用的是ARK_METHOD_METADATA(同步),而不是ARK_ASYNC_METHOD_METADATA(异步/返回 Promise)。
4.5 更新 C++ Package 注册
generated/RNOHGeneratedPackage.h — 新增 RNVersionNumber:
/**
* This code was generated by "react-native codegen-harmony"
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*
* @generatorVersion: 1
*/
#pragma once
#include "RNOH/Package.h"
#include "RNOH/ArkTSTurboModule.h"
#include "generated/TTSNativeModule.h"
#include "generated/RNVersionNumber.h" // ← 新增
namespace rnoh {
class RNOHGeneratedPackageTurboModuleFactoryDelegate : public TurboModuleFactoryDelegate {
public:
SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override {
if (name == "TTSNativeModule") {
return std::make_shared<TTSNativeModule>(ctx, name);
}
if (name == "RNVersionNumber") { // ← 新增
return std::make_shared<RNVersionNumber>(ctx, name);
}
return nullptr;
};
};
// ... 其余代码不变
4.6 新增文件总览
| 文件路径 | 类型 | 说明 |
|---|---|---|
ets/generated/turboModules/RNVersionNumber.ts |
ETS | TM 类型定义(NAME + Spec 接口) |
cpp/generated/RNVersionNumber.h |
C++ | TurboModule 声明 |
cpp/generated/RNVersionNumber.cpp |
C++ | TurboModule 实现 + 方法映射表 |
修改的文件:
| 文件路径 | 修改内容 |
|---|---|
ets/generated/turboModules/ts.ts |
新增 export * from "./RNVersionNumber" |
cpp/generated/RNOHGeneratedPackage.h |
新增 #include 和 if (name == "RNVersionNumber") 分支 |
阶段五:C++ 原生层配置
5.1 更新 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)
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
add_library(rnoh_app SHARED
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
"${RNOH_GENERATED_DIR}/RNOHGeneratedPackage.h"
"${RNOH_GENERATED_DIR}/TTSNativeModule.cpp"
"${RNOH_GENERATED_DIR}/RNVersionNumber.cpp" # ← 新增
)
target_include_directories(rnoh_app PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}"
"${RNOH_GENERATED_DIR}"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
💡 相比之前的 TTS 集成,仅新增了
"${RNOH_GENERATED_DIR}/RNVersionNumber.cpp"一行。
5.2 PackageProvider.cpp — 无需修改
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
};
}
💡
RNOHGeneratedPackage内部已经注册了RNVersionNumber,所以PackageProvider.cpp无需修改。
5.3 更新 RNPackagesFactory.ets
import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
import { RNTTSPackage } from './tts/RNTTSPackage';
import { RNVersionNumberPackage } from './version_number/RNVersionNumberPackage.ets';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
new RNTTSPackage(ctx),
new RNVersionNumberPackage(ctx), // ← 新增
];
}
阶段六:App 业务代码与 Bundle 构建
6.1 App.tsx
在原有 TTS Demo 的基础上,新增版本信息展示卡片:
import React, { useState, useEffect } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
ScrollView,
Alert,
} from 'react-native';
import Tts from '@react-native-ohos/react-native-tts';
import VersionNumber from 'react-native-version-number';
function App() {
const [text, setText] = useState('你好,鸿蒙!欢迎使用 React Native 语音合成功能。');
const [status, setStatus] = useState('就绪');
const [isSpeaking, setIsSpeaking] = useState(false);
const [rate, setRate] = useState(1.0);
const [pitch, setPitch] = useState(1.0);
useEffect(() => {
Tts.getInitStatus().then(() => {
setStatus('TTS 引擎已就绪');
}).catch((err) => {
setStatus('TTS 引擎初始化失败: ' + JSON.stringify(err));
});
const onStart = Tts.addEventListener('tts-start', (event) => {
setIsSpeaking(true);
setStatus('正在朗读...');
});
const onFinish = Tts.addEventListener('tts-finish', (event) => {
setIsSpeaking(false);
setStatus('朗读完成');
});
const onError = Tts.addEventListener('tts-error', (event) => {
setIsSpeaking(false);
setStatus('朗读出错');
});
const onCancel = Tts.addEventListener('tts-cancel', (event) => {
setIsSpeaking(false);
setStatus('已停止');
});
return () => {
onStart.remove();
onFinish.remove();
onError.remove();
onCancel.remove();
};
}, []);
const handleSpeak = () => {
if (!text.trim()) {
Alert.alert('提示', '请输入要朗读的文本');
return;
}
Tts.speak(text);
};
const handleStop = () => {
Tts.stop(true);
};
const handleRateChange = (delta) => {
const newRate = Math.max(0.5, Math.min(2.0, rate + delta));
setRate(newRate);
Tts.setDefaultRate(newRate);
};
const handlePitchChange = (delta) => {
const newPitch = Math.max(0.5, Math.min(2.0, pitch + delta));
setPitch(newPitch);
Tts.setDefaultPitch(newPitch);
};
return (
<ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
<Text style={styles.title}>RN OHOS Demo</Text>
{/* 版本信息卡片 */}
<View style={styles.card}>
<Text style={styles.cardTitle}>应用版本信息</Text>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>应用版本</Text>
<Text style={styles.infoValue}>{VersionNumber.appVersion || 'N/A'}</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>构建版本</Text>
<Text style={styles.infoValue}>{VersionNumber.buildVersion || 'N/A'}</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>包名标识</Text>
<Text style={styles.infoValue}>{VersionNumber.bundleIdentifier || 'N/A'}</Text>
</View>
</View>
{/* TTS 卡片 */}
<View style={styles.card}>
<Text style={styles.cardTitle}>语音合成</Text>
{/* ... TTS 相关 UI ... */}
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f2f5',
},
contentContainer: {
padding: 16,
paddingBottom: 40,
},
title: {
fontSize: 22,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 16,
color: '#1a1a1a',
},
card: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
elevation: 2,
},
cardTitle: {
fontSize: 17,
fontWeight: '600',
color: '#333',
marginBottom: 12,
},
infoRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 6,
borderBottomWidth: 1,
borderBottomColor: '#f0f0f0',
},
infoLabel: {
fontSize: 14,
color: '#666',
},
infoValue: {
fontSize: 14,
color: '#1a1a1a',
fontWeight: '500',
},
// ... 其他样式 ...
});
export default App;
💡
VersionNumber.appVersion、VersionNumber.buildVersion、VersionNumber.bundleIdentifier三个属性在模块加载时就会通过getConstants()获取,无需异步调用。
6.2 构建 JS Bundle
cd AwesomeProject
npx react-native bundle-harmony --dev false

输出确认重定向:
[INFO] Redirected imports to 2 harmony-specific third-party package(s):
[INFO] • react-native-tts → @react-native-ohos/react-native-tts
[INFO] • react-native-version-number → @react-native-ohos/react-native-version-number
info Created harmony/entry/src/main/resources/rawfile/bundle.harmony.js
6.3 复制 Bundle 到 OHOS 工程
cp AwesomeProject/harmony/entry/src/main/resources/rawfile/bundle.harmony.js \
Myrndemo/entry/src/main/resources/rawfile/bundle.harmony.js
阶段七:构建部署与验证
7.1 构建 HAP
cd Myrndemo
export RNOH_C_API_ARCH=1
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
--mode module -p module=entry@default -p product=default assembleHap --no-daemon

构建成功输出:
> hvigor BUILD SUCCESSFUL in 6 s 945 ms
7.2 部署到设备
hdc install -r Myrndemo/entry/build/default/outputs/default/entry-default-signed.hap
输出:
[Info] App install path:... msg:install bundle successfully.
7.3 启动应用
hdc shell aa start -a EntryAbility -b com.nutpi.rndemo
7.4 验证日志
hdc hilog -x | grep -iE "RNVersionNumber|bundleInfo"
成功日志示例:
RNVersionNumber, {"name":"com.nutpi.rndemo","versionName":"1.0.0","versionCode":1000000}
7.5 应用截图
应用运行后,"应用版本信息"卡片中展示了三行数据:
| 字段 | 值 |
|---|---|
| 应用版本 | 1.0.0 |
| 构建版本 | 1000000 |
| 包名标识 | com.nutpi.rndemo |

四、完整文件变更清单
| 文件路径 | 变更类型 | 说明 |
|---|---|---|
AwesomeProject/package.json |
修改 | 新增 @react-native-ohos/react-native-version-number 依赖 |
AwesomeProject/App.tsx |
修改 | 新增 VersionNumber 版本信息展示卡片 |
Myrndemo/entry/src/main/ets/version_number/RNVersionNumberPackage.ets |
新增 | RN Package 注册 |
Myrndemo/entry/src/main/ets/version_number/RNVersionNumberTurboModule.ts |
新增 | TurboModule 桥接 |
Myrndemo/entry/src/main/ets/version_number/Logger.ts |
新增 | 日志工具 |
Myrndemo/entry/src/main/ets/RNPackagesFactory.ets |
修改 | 注册 RNVersionNumberPackage |
Myrndemo/entry/src/main/ets/generated/turboModules/RNVersionNumber.ts |
新增 | ETS TM 类型定义 |
Myrndemo/entry/src/main/ets/generated/turboModules/ts.ts |
修改 | 新增 RNVersionNumber 导出 |
Myrndemo/entry/src/main/cpp/generated/RNVersionNumber.h |
新增 | C++ TurboModule 声明 |
Myrndemo/entry/src/main/cpp/generated/RNVersionNumber.cpp |
新增 | C++ TurboModule 实现 |
Myrndemo/entry/src/main/cpp/generated/RNOHGeneratedPackage.h |
修改 | 新增 RNVersionNumber 注册 |
Myrndemo/entry/src/main/cpp/CMakeLists.txt |
修改 | 新增 RNVersionNumber.cpp 源文件 |
五、关键决策说明
决策 1:选择 0.5.0-beta.1 版本
选择:@react-native-ohos/react-native-version-number@0.5.0-beta.1
理由:
- 项目使用 RN 0.82.1 + RNOH 0.82.30,需要对应 0.82.x 的适配版本
- 0.4.0 版本对应 RN 0.77.x,不支持 Autolink
- 虽然 0.5.0-beta.1 是 beta 版,但功能简单(仅
getConstants()),风险可控
决策 2:源码集成 vs .har 依赖
选择:源码直接集成
理由:
.har路径依赖在不同构建环境下经常出现路径解析错误- 源码集成便于调试(可以直接在源码中打断点)
- 仅 3 个文件,集成成本极低
决策 3:Codegen 桥接代码手动生成
选择:手动创建 ETS/C++ 桥接文件
理由:
@react-native-ohos/react-native-version-number的NativeRNVersionNumber.ts缺少@codegenConfig注解- Codegen 扫描不到该 Spec,无法自动生成桥接代码
- 该 TurboModule 只有一个
getConstants()方法,手动创建文件工作量极小
决策 4:getConstants 使用 ARK_METHOD_METADATA 而非 ARK_ASYNC_METHOD_METADATA
选择:ARK_METHOD_METADATA(getConstants, 0)
理由:
getConstants()是同步方法,不返回 Promise- 在 JS 端的
index.js中,TurboModuleRegistry.get('RNVersionNumber').getConstants()是同步调用 - 如果误用
ARK_ASYNC_METHOD_METADATA,getConstants()会返回 Promise 而非对象,导致 JS 端解构失败
六、踩坑与排障记录
问题 1:Codegen 不生成 RNVersionNumber 桥接代码
现象:执行 react-native codegen-harmony 后只生成了 TTS 的文件
原因:npm 包中的 NativeRNVersionNumber.ts 没有使用 @codegenConfig 注解,Codegen 扫描不到
解决:手动创建 RNVersionNumber.ts(ETS 类型)、RNVersionNumber.h/.cpp(C++ 桥接),并更新 ts.ts 和 RNOHGeneratedPackage.h
问题 2:ArkTS 编译报错 “has already exported a member named ‘RNVersionNumber’”
现象:
Error Message: Module "./TTSNativeModule" has already exported a member named 'RNVersionNumber'.
Consider explicitly re-exporting to resolve the ambiguity.
At File: .../generated/turboModules/ts.ts:9:1
原因:在手动向 TTSNativeModule.ts 中添加 RNVersionNumber namespace 的同时,又创建了独立的 RNVersionNumber.ts 文件并在 ts.ts 中导出,导致同名导出冲突
解决:从 TTSNativeModule.ts 中移除 RNVersionNumber namespace,只保留独立的 RNVersionNumber.ts 文件
问题 3:import 路径需要从 ./generated/ts 改为 ../generated/ts
现象:ArkTS 编译报错找不到模块
原因:npm 包中的源码 import 路径 ./generated/ts 是相对于库内部结构的,复制到项目后目录层级不同
解决:修改 RNVersionNumberTurboModule.ts 和 RNVersionNumberPackage.ets 中的 import 路径:
- import { TM } from "./generated/ts";
+ import { TM } from '../generated/ts';
七、数据流全景图
┌─────────────────────────────────────────────────────────────────┐
│ JS 层 (App.tsx) │
│ │
│ import VersionNumber from 'react-native-version-number' │
│ │ │
│ │ Metro 重定向 │
│ ▼ │
│ @react-native-ohos/react-native-version-number/index.js │
│ │ │
│ │ TurboModuleRegistry.get('RNVersionNumber') │
│ │ .getConstants() │
│ ▼ │
├────────────────────────── C++ 层 ───────────────────────────────┤
│ │
│ RNOHGeneratedPackage │
│ └─ createTurboModule("RNVersionNumber") │
│ │ │
│ ▼ │
│ RNVersionNumber (ArkTSTurboModule) │
│ └─ methodMap_: { getConstants → ARK_METHOD_METADATA } │
│ │ │
│ │ 调用 ArkTS 端 │
│ ▼ │
├────────────────────────── ArkTS 层 ─────────────────────────────┤
│ │
│ RNVersionNumberTurboModule.getConstants() │
│ │ │
│ │ bundleManager.getBundleInfoForSelfSync() │
│ ▼ │
│ 返回 { RNVersionNumber: { appVersion, buildVersion, │
│ bundleIdentifier } } │
│ │
└─────────────────────────────────────────────────────────────────┘
八、与 react-native-tts 集成的对比
| 维度 | react-native-tts | react-native-version-number |
|---|---|---|
| 源码文件数 | 5 个 | 3 个 |
| TurboModule 方法数 | 18 个 | 1 个 (getConstants) |
| C++ 方法映射 | ARK_ASYNC_METHOD_METADATA 为主 |
ARK_METHOD_METADATA(同步) |
| Codegen 自动生成 | ✅ 成功 | ❌ 需手动 |
| 事件发射 | ✅ 需要 emitDeviceEvent |
❌ 无 |
| 第三方鸿蒙 API | @kit.CoreSpeechKit |
@kit.AbilityKit |
| 集成难度 | ⭐⭐⭐⭐ | ⭐⭐ |
九、总结
将 react-native-version-number 集成到 HarmonyOS NEXT 平台的核心流程可概括为 “源码复制 + 手动桥接”:
- JS 端:安装
@react-native-ohos/react-native-version-number@0.5.0-beta.1,Metro 自动重定向 import - ArkTS 端:复制 3 个源文件,修改 import 路径,实现
getConstants()调用bundleManager - C++ 端:手动创建
RNVersionNumber.h/.cpp,注册getConstants方法映射,更新RNOHGeneratedPackage.h
最关键的三个注意事项:
- 版本对齐:必须选择与 RN 版本匹配的鸿蒙适配版本(0.82.x → 0.5.0-beta.1)
- Codegen 可能不自动生成:如果 Spec 缺少
@codegenConfig注解,需手动创建桥接文件 - 同步 vs 异步方法:
getConstants()是同步方法,C++ 侧必须使用ARK_METHOD_METADATA,不能用ARK_ASYNC_METHOD_METADATA
参考文档
更多推荐


所有评论(0)