React Native for OpenHarmony 实战:三方库 react-native-device-name 的鸿蒙化适配指南
react-native-device-name 做的事很小:读一个"用户能看到的设备型号名"。埋点上报、设备列表、客服排查、多端登录设备管理这类场景会用到它。
它的公开 API 只有一个方法,但它是必须做原生适配的那类库——因为它的 JavaScript 只是一层壳。上游的入口文件只有三行:
import { NativeModules } from 'react-native';
const { DeviceName } = NativeModules;
export default DeviceName;
真正的实现在上游的 ios/DeviceName.m 和 android/src/main/java/com/reactlibrary/DeviceNameModule.java 里。没有鸿蒙实现,所以在鸿蒙上这个原生模块根本注册不出来。
本文讲清四件事:怎么快速判断一个库"必须有原生实现"、适配要补什么、一个返回"设备相关非固定值"的接口该怎么验证、以及交付包里几处可以补的文档缺口。
环境准备:本文不重复环境搭建步骤。RNOH(React Native for OpenHarmony)开发环境的完整配置见官方开发者指南:
https://atomgit.com/CPF-RN/docs/blob/main/开发者指南/02-搭建准备/环境初始化.md

一、先说结论:这是一个「JS 只是壳」的库
| 项 | 结论 |
|---|---|
| 需要原生适配吗 | ✅ 需要 |
| 适配要补什么 | 一个鸿蒙原生模块(HAR + ArkTS TurboModule) |
| 补的体量 | ArkTS 侧 8 行;HAR 源码工程 9 个文件、预编译产物 2.6 KB |
| 需要权限吗 | ❌ 不需要 |
| 公开 API | 只有 1 个:getDeviceName(): Promise<string> |
判断依据很直接:看 package.json 有没有 harmony.autolinking。
"harmony": {
"alias": "react-native-device-name",
"autolinking": {
"ohPackageName": "@react-native-ohos/react-native-device-name",
"etsPackageClassName": "DeviceNamePackage",
"cppPackageClassName": "DeviceNamePackage",
"cmakeLibraryTargetName": "rnoh_device_name"
}
}
二、判定过程:怎么确认必须做原生适配
选库阶段就能判断,不用先装进来。
第一步:看 package.json 有没有 harmony 字段
npm view <包名> harmony --json
有 harmony.autolinking(ohPackageName / etsPackageClassName / cppPackageClassName / cmakeLibraryTargetName 那四个名字)的,一定是带原生实现的包,适配时要做 link-harmony + ohpm + hvigor 三件事。
没有这个字段的可能是纯 JS 库,也可能像本例一样——上游没做鸿蒙适配,所以字段是后加的。所以要继续看第二步。
第二步:看上游包里有哪些平台的实现
$ npm pack react-native-device-name@1.0.0
$ tar -xzf react-native-device-name-1.0.0.tgz
$ ls package
index.js package.json README.md
android/ ios/ react-native-device-name.podspec
android/ 和 ios/ 两个目录都在,但没有 harmony/ —— 这就是"必须补一个鸿蒙实现"的直接证据。
第三步:读入口文件,看它到底在做什么
// package/index.js —— 上游的全部 JS
import { NativeModules } from 'react-native';
const { DeviceName } = NativeModules;
export default DeviceName;
入口里没有任何算法,只是在取一个原生模块。 这类库的"本体"是原生实现,JS 那层换个平台就得靠新的原生模块顶上去。
反过来,如果入口里是完整的业务逻辑(字符串处理、编解码、状态机),只是偶尔
Platform.OS === 'ios'分叉一下——那通常是"纯 JS 库 + 平台差异",优先考虑在 JS 侧解决,不一定要写原生。
三、适配实现:取值链与协作方式
交付版新增的 harmony/device_name/ 结构:
harmony/device_name/
├── Index.ets # 导出 DeviceNamePackage
├── oh-package.json5 # 声明包名 @react-native-ohos/react-native-device-name
├── build-profile.json5
└── src/main/
├── module.json5
├── cpp/ # CAPI 架构下的 C++ 侧(只是个注册壳)
│ ├── CMakeLists.txt
│ ├── DeviceNamePackage.h # Package + TurboModule 工厂 + methodMap
│ └── DeviceNamePackage.cpp
└── ets/
├── DeviceNamePackage.ets # 把 TurboModule 交给 RNOH
└── DeviceNameTurboModule.ts # ★ 全部实现逻辑
ArkTS 侧的全部实现只有一行取值逻辑:
import {UITurboModule} from '@rnoh/react-native-openharmony/ts';
import deviceInfo from '@ohos.deviceInfo';
export class DeviceNameTurboModule extends UITurboModule {
async getDeviceName(): Promise<string> {
return deviceInfo.marketName || deviceInfo.productModel || 'OpenHarmony device';
}
}
取值链:marketName → productModel → 兜底文本 'OpenHarmony device'。
marketName是面向用户的市场名(真机上通常是 “HUAWEI Mate 60” 这类),业务展示应该优先用它;productModel是产品型号,在模拟器或未配置市场名的设备上更可能有值;- 最后一层是兜底,保证接口永远返回非空字符串——因为契约是
Promise<string>、调用方通常直接展示。
@ohos.deviceInfo 读的是系统公开参数,不需要任何权限。
C++ 侧只是个注册壳,一行业务逻辑都没有:
class DeviceName : public ArkTSTurboModule {
public:
DeviceName(const ArkTSTurboModule::Context ctx, const std::string& name) : ArkTSTurboModule(ctx, name) {
methodMap_ = {
ARK_ASYNC_METHOD_METADATA(getDeviceName, 0),
};
}
};
ARK_ASYNC_METHOD_METADATA(getDeviceName, 0) —— 方法名、0 个参数、返回 Promise。方法名或参数个数写错,表现是"编译过了但调用报方法不存在"。
JS 侧的改动:把 undefined 隐患换成"早失败"
交付版的 src/index.ts 相比上游换了取模块的方式:
import {TurboModuleRegistry, type TurboModule} from 'react-native';
export interface Spec extends TurboModule {
getDeviceName(): Promise<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('DeviceName');
上游是 const { DeviceName } = NativeModules——模块没注册时得到 undefined,要等到第一次调用才炸。交付版用 TurboModuleRegistry.getEnforcing,模块缺失时在导入阶段就抛出明确错误。
这个改动很小,但对排障很有用:接入出错时你会在启动日志里立刻看到,而不是在业务跑到某个分支时才报 Cannot read property 'getDeviceName' of undefined。
四、接入宿主:三处改动面(外加一处自动生成的)
库本身不能独立运行,必须有一个 RNOH 宿主 App。这里用社区现成的 RNOH084Demo(RNOH 0.84.3 的多库验证宿主),它自带一个 rnAppKey 机制,一个宿主可以挂很多独立测试页:
// harmony/entry/src/main/ets/entryability/EntryAbility.ets
const rnAppKey = want.parameters?.['rnAppKey'] as string | undefined;
AppStorage.setOrCreate('rnAppKey', rnAppKey ?? 'RNOH084Demo');
于是可以用命令行参数切换测试页:
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey DeviceNameTestApp
接入要改的地方
第一处:package.json。
"react-native-device-name": "file:../react-native-device-name"
第二处:两级 oh-package.json5 都要写 HAR。
"@react-native-ohos/react-native-device-name":
"file:../node_modules/react-native-device-name/harmony/device_name.har",
harmony/oh-package.json5 管工程级、harmony/entry/oh-package.json5 管模块级,两处都要加。只加一处会出现"能找到包但链接不上"。
这里有个很容易漏的点:跑 link-harmony 时,它只会自动更新工程级那一份,模块级那份要你自己加。
第三处:在 ETS 侧注册 Package。
// harmony/entry/src/main/ets/RNOHPackagesFactory.ets
import type { RNPackageContext, RNOHPackage } from '@rnoh/react-native-openharmony';
import DeviceNamePackage from '@react-native-ohos/react-native-device-name';
export function createRNOHPackages(ctx: RNPackageContext): RNOHPackage[] {
return [
new DeviceNamePackage(ctx),
];
}
代码写在哪,这里说清楚:手工改动面就是这三个文件(外加 metro.config.js 的 watchFolders)。C++ 侧不用手改——CAPI 架构下 PackageProvider.cpp 会自动消费 autolinking 生成的 RNOHPackagesFactory.h。
那"自动生成的一处"是什么? 执行 link-harmony 时,它会一次性重写这四个文件:
• harmony/entry/src/main/cpp/RNOHPackagesFactory.h # C++ 侧注册
• harmony/entry/src/main/cpp/autolinking.cmake # add_subdirectory + 链接
• harmony/entry/src/main/ets/RNOHPackagesFactory.ets # ETS 侧注册
• harmony/oh-package.json5 # 工程级 HAR 依赖
这四个文件头部都写着 DO NOT modify it manually, your changes WILL be overwritten.——别手改。
接入成功的两个自检信号
$ node_modules\.bin\react-native link-harmony --verbose
[link] react-native-device-name
[skip] @react-native-oh/react-native-harmony
...
info updated 4 file(s), linked 6 libraries, skipped 1 library
[link] react-native-device-name 在列表里,说明 autolinking 认出了它的 harmony.autolinking。打包时 Metro 也会把它列进重定向清单:
[INFO] Redirected imports to 6 harmony-specific third-party package(s):
[INFO] • react-native-device-name → react-native-device-name
…
这两个信号对这个库是"必须出现":有原生实现的包要被解析到它的鸿蒙实现,列表里没有它就说明没接通。
(harmony/entry/src/main/module.json5 未改动——本库不读任何需要权限的信息,不用加 requestPermissions。)
五、构建与运行
# 1) 装 JS 依赖 + 自动链接
npm install
./node_modules/.bin/react-native link-harmony
# 2) 生成调试签名 + 装 ohpm 依赖
cd harmony
devecocli signature generate
ohpm install --all
# 3) 打包 JS bundle(输出到 harmony/entry/src/main/resources/rawfile/)
cd ..
npm run dev
# 4) 编译 HAP
cd harmony
hvigorw --mode module -p product=default -p module=entry@default assembleHap --no-daemon
# 5) 安装 + 启动测试页
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
# 换页参数只在「冷启动」时生效:先 force-stop 再起
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey DeviceNameTestApp
耗时:在已有原生编译缓存的宿主上增量加入这个库,assembleHap 用了 6 分 35 秒;HAP 从 80.09 MB 涨到 80.23 MB(约 +136 KB)。
如果宿主是全新 clone(没有原生编译缓存),首次构建会到 30–40 分钟量级;只改 JS 重新打包也要 6–7 分钟,所以别把 UI 微调留到最后做。
看日志:
hdc shell "hilog -x | grep -i 'TM created'"
hdc shell "hilog -x | grep -i 'device-name-test'"
看界面(读无障碍树,不用截图就能拿到文本):
devecocli ui layout
按文本点击(页面高度会随结果卡片变化,别记固定坐标):
. E:\rnoh-work\click-label.ps1
Click-Label -Pattern '读取设备名并跑全部断言'
六、验证设计:设备相关的非固定值怎么验
这个库只有一个方法,而且返回值不是固定值——它取决于跑在什么设备上。
这就带来一个验证陷阱:如果只断言"返回非空字符串",那么返回 "a"、"随便什么"、"OpenHarmony device" 都会通过——等于没验证。
所以验证要回答两个不同的问题:
| 问题 | 怎么验 |
|---|---|
| 接口形态对不对 | 返回 Promise、类型是 string、非空、无空白/控制字符、长度合理、多次调用一致 |
| 返回值对不对 | 与设备系统参数交叉核验(期望值由外部独立读出) |
期望值从哪来:用 hdc 独立读系统参数
$ hdc shell param get const.product.marketname
Get parameter "const.product.marketname" fail! errNum is:106! ← 不存在
$ hdc shell param get const.product.model
emulator
$ hdc shell param get const.product.name
emulator
$ hdc shell param get const.product.brand
HUAWEI
$ hdc shell param get const.product.os.dist.name
HarmonyOS
$ hdc shell param get const.product.os.dist.apiname
26.0.0
$ hdc shell param get const.product.software.version.name
HarmonyOS NEXT Developer Beta1
这一步是整个验证的关键:期望值来自系统本身(param get),不是从被测库拿的。如果库返回一个自己编的字符串,它不可能同时等于 const.product.model 和 const.product.name。
而且这次还额外覆盖到了回退分支:本机 const.product.marketname 不存在,也就是 deviceInfo.marketName 为空,所以实现必然走第二条分支取 productModel → 期望返回值是 "emulator"。
⇒ 一次验证同时确认了两件事:
- 值是对的(等于系统参数给出的
productModel); - 回退分支真的生效了(
marketName为空时确实回退,而不是返回空串或抛错)。
七、真机验证
验证环境:Pura X View 模拟器,HarmonyOS 7.0.0(26.0.0) Beta2,API 26,ohos-x64。
TurboModule 注册
RNInstance::TurboModuleProvider TM created: DeviceName

断言结果:10 / 10 全部通过
A 组:契约与形态(7 项)
| 断言 | 结果 |
|---|---|
返回 Promise(typeof .then === 'function') | ✅ |
返回类型是 string | ✅ |
| 非空字符串 | ✅ |
| 无首尾空白 | ✅ |
无控制字符(/[\u0000-\u001f\u007f]/ 不匹配) | ✅ |
| 长度不超过 64 | ✅ |
| 连续三次调用结果一致 | ✅ |
B 组:值正确性(3 项)
| 断言 | 结果 |
|---|---|
等于 const.product.model(emulator) | ✅ |
等于 const.product.name(emulator) | ✅ |
未落到兜底文本 'OpenHarmony device' | ✅ |

原始读数与耗时
[device-name-test] getDeviceName() 连续三次:"emulator" / "emulator" / "emulator"
[device-name-test] 单次耗时:31 ms / 4 ms / 0 ms
[device-name-test] A + B(契约 / 形态 / 值正确性) -> 10/10 全部通过
首次调用 31 ms(含 TurboModule 创建与 ArkTS 侧首次加载 @ohos.deviceInfo),之后 4 ms / 0 ms。这是个"读一次就能缓存"的接口,业务侧没必要反复调用。
能力对照
| 能力 | 结果 |
|---|---|
getDeviceName() 返回 Promise | ✅ |
| 返回非空字符串 | ✅ |
返回值等于设备系统参数中的 productModel | ✅ 独立交叉核验通过 |
marketName 为空时的回退分支 | ✅ 本机实测走的就是这条分支 |
| 需要权限 | ✅ 不需要(宿主未新增任何 requestPermissions) |
| TurboModule 注册 | ✅ TM created: DeviceName |
八、交付包可补的地方
运行时没有问题,但交付包在可追溯性上有三处缺口,如实记下来:
一、README 里的上游地址是占位符。 README.OpenHarmony_CN.md / README.OpenHarmony.md 写的都是 github_account/react-native-device-name——这不是真实上游地址,读者按 README 找不到源仓库。应该换成上游真实地址。
二、spec.json 没记录上游仓库与基线 commit。 现有一份 spec.json 只有 name / version / module / native / description / methods / rnoh / reactNative / validation,没有 upstream 字段、也没有 upstreamCommit。对交付包来说,“我验的是上游哪一版"是核心信息——缺了它就无法复现,也无法回答"上游发新版后这版适配是否还对应”。
三、契约测试没有真正测到东西。
const test = require('node:test');
const assert = require('node:assert/strict');
test('public contract returns a promise of a non-empty device name', () => {
assert.equal(typeof 'DeviceName', 'string');
assert.equal(typeof Promise.resolve('OpenHarmony device').then, 'function');
});
这两条断言的对象是字面量——typeof 'DeviceName' 永远是 'string',typeof Promise.resolve(...).then 永远是 'function'。测试没有 import 本库,也没有调用任何方法,所以无论库是否可用都会通过。这不是验证,只是占位。
根因推测:上游 npm 包本身就是没改过的 RN 库模板——package/package.json 里 "description": "TODO"、"author": {"name": "Your Name", "email": "yourname@email.com"}、repository.url 指向 github_account/...。交付时把上游的占位符照抄进了 README,也没另外补 spec.json 的上游字段。
顺带一个上游打包问题:上游 tarball 92,757 字节里塞满了构建产物——android/build/、android/.gradle/、android/.idea/(其中 gradle_models.ser 56 KB、executionHistory.bin 32 KB)。这是上游 .npmignore 配置的问题,不是交付方引入的,但会让包体积膨胀十几倍。
九、已知限制
一、只读系统公开字段,不读用户自定义名称。 实现取的是 deviceInfo.marketName / deviceInfo.productModel,不是用户在设置里改过的设备名。如果你的业务需要"用户给设备起的名字",这个库不满足——那通常要调设置类接口,而且往往需要权限。
二、不同 ROM 的字段取值可能不同。 const.product.marketname / const.product.model 是否存在、格式如何,取决于 ROM。本次只在一台模拟器上验证,实测 marketName 为空、回退到 productModel。
三、回退到兜底文本的分支未实测。 只有 marketName 与 productModel 同时为空时才会返回 'OpenHarmony device'。模拟器上至少有一个有值,无法构造这个场景——所以"兜底文本"这条路径只有代码层面可见,没有设备侧证据。
四、真机上的返回值很可能与模拟器不同。 真机通常有市场名(如 “HUAWEI Mate 60”),届时返回的会是 marketName 而不是 productModel。业务展示应该允许名称变化,不要把它当作稳定标识符做逻辑判断。
五、返回值不是设备唯一标识。 同一个型号名会出现在成千上万台设备上。它只适合展示,不适合做设备身份——需要唯一标识时应该用别的方案。
六、未测大并发与高频调用。 接口本身没有缓存,每次调用都会走过桥。实测首次 31 ms、后续 0–4 ms;业务侧建议自己缓存结果,不要放在高频渲染路径里。
七、其他 ROM / 真机未验证。 适配方记录的是 OpenHarmony-7.0.0.105;本次在 HarmonyOS 7.0.0(26.0.0) Beta2 模拟器上通过。
十、常见问题
Q:这个库为什么必须做原生适配?
A:它的 JS 入口只有三行——export default NativeModules.DeviceName,本体是原生实现。上游只提供了 ios/DeviceName.m 和 android/.../DeviceNameModule.java,没有鸿蒙实现。不补一个鸿蒙原生模块,NativeModules.DeviceName 就是 undefined,调用必然失败。
Q:怎么快速判断一个库要不要做原生适配?
A:三步。① npm view <包名> harmony --json 看有没有 harmony.autolinking(有 → 一定是带原生实现的适配包);② 上游包里有哪些平台的实现目录(只有 ios/ + android/ 而没有 harmony/ → 要补一个鸿蒙实现);③ 读入口文件——如果里面只有"取原生模块"而没有任何业务逻辑,那它天生依赖原生。
Q:适配一共要改多少东西?
A:ArkTS 侧的实现逻辑只有一行(deviceInfo.marketName || deviceInfo.productModel || 'OpenHarmony device'),加上 Package/CMake/Index 的接线一共 9 个文件,预编译 HAR 只有 2.6 KB。接入宿主时手工改动面是三处:package.json 依赖、模块级 oh-package.json5 加 HAR、RNOHPackagesFactory.ets 注册 Package。
Q:为什么返回的是 "emulator" 而不是设备市场名?
A:因为实现的取值链是 marketName → productModel → 兜底文本,而这台模拟器的 marketName 为空(hdc shell param get const.product.marketname 返回不存在),所以回退到了 productModel(emulator)。真机上通常有市场名,返回值会不同——这也是为什么验证不能只断言"非空"。
Q:怎么验证一个"返回值不固定"的接口?
A:把验证拆成两层:① 形态层——返回 Promise、类型、非空、无空白/控制字符、多次一致;② 值层——找一个外部独立来源作为期望值。本例用的是 hdc shell param get const.product.model,从系统本身读出期望值,而不是从被测库拿。如果库返回的是自己编的字符串,它不可能同时等于两个不同的系统参数。
Q:返回的设备名能当唯一标识用吗?
A:不能。同一个型号名会出现在大量设备上。它只适合展示。需要唯一标识请用别的方案。
Q:能不能读用户自己改的设备名?
A:不能。实现只读 @ohos.deviceInfo 的系统公开字段,不读用户自定义名称,也不需要任何权限。业务如果依赖用户自定义名称,得另找接口。
Q:spec.json 里没有上游 commit,会不会有问题?
A:不影响运行,但影响可追溯性。交付包的价值之一是能回答"我验的是上游哪一版、上游发新版后这版是否还对得上"。缺 upstream / upstreamCommit 就答不了。README 里那个 github_account/... 也是占位符,建议一并补上。
Q:为什么我在模拟器上编译要这么久?
A:宿主已有原生编译缓存时,增量加一个库约 6–7 分钟(hvigor 会把整套流水线走一遍);全新克隆的宿主首次编译要 30–40 分钟。只改 JS 重新打包也是 6–7 分钟。
小结
这个库的适配体量是"一行取值 + 三层接线",但有两件事值得记:
第一件是判定。 一个 RN 三方库要不要做原生适配,看三点就够了:package.json 有没有 harmony.autolinking、上游包里有哪些平台的实现目录、入口文件里有没有真实业务逻辑。本例的入口只有三行、只是在取一个原生模块——这类库的本体在原生侧,换个平台就必须补实现。判断对了,就清楚知道工作量在哪;判断错了(比如把一个"JS 只是壳"的库当纯 JS 用),会在运行时才发现模块是 undefined。
第二件是"非固定返回值"的验证设计。 这个库只有一个方法、返回一个随设备变化的字符串。只断言"非空"等于没验证——任何字符串都能过。所以要把验证拆成两层:形态层查契约,值层找外部独立来源做交叉核验。本例用的是 hdc shell param get:期望值由系统读出,不经由被测库。这样即使库返回一个自造的字符串,也无法同时等于两个不同的系统参数。
而且这次运气不错:本机 marketName 恰好为空,于是实测正好走过了"回退到 productModel"这条分支——一次验证同时确认了"值是对的"和"回退逻辑真的生效"。验证一个带取值链的接口时,先弄清每条分支的触发条件,再挑一个能触发非首条分支的环境,比只测 happy path 有价值得多。
最后如实记了交付包的三处文档缺口:README 里上游地址还是占位符、spec.json 缺上游 commit、契约测试断言的是字面量(没 import 库、没调用方法,怎么跑都会通过)。这三条不影响运行时,但影响"这个交付包能不能被信任和复现"——一个测试如果永远不会失败,它就不是测试。
本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-device-name(上游 1.0.0 的鸿蒙适配版) |
| 交付仓库 | https://atomgit.com/oh-react-native/react-native-device-name |
| 适配 TAG | 1.0.0-ohos-1.0.0 |
| ohpm 包名 | @react-native-ohos/react-native-device-name |
| HAR | harmony/device_name.har(2.6 KB) |
| 原生模块名 | DeviceName |
| 是否需要权限 | 不需要 |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = DeviceNameTestApp) |
"react-native-device-name": "git+https://atomgit.com/oh-react-native/react-native-device-name.git#1.0.0-ohos-1.0.0"
// harmony/oh-package.json5 与 harmony/entry/oh-package.json5 都要加
"@react-native-ohos/react-native-device-name":
"file:../node_modules/react-native-device-name/harmony/device_name.har",
import DeviceName from 'react-native-device-name';
const name = await DeviceName.getDeviceName(); // 例如 "emulator" / "HUAWEI Mate 60"
# 换页启动测试页(force-stop 不能省,换页参数只在冷启动生效)
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey DeviceNameTestApp
验证环境
| 项 | 版本 |
|---|---|
| React Native | 0.84.1 |
| React | 19.2.3 |
| RNOH(npm / ohpm) | @react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3 |
| Node.js | v24.14.0 |
| DevEco Studio | 26.0.0.621 |
| HarmonyOS SDK | API 26(26.0.0.32) |
| 设备 | HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64) |
本机 const.product.model | emulator |
本机 const.product.marketname | 不存在(errNum 106) |
| 宿主 HAP 产物 | entry-default-signed.hap(80.23 MB) |
| 本次增量构建 | assembleHap 6 分 35 秒 |
| 验证规模 | 设备侧 10 项断言全通过 + 与系统参数交叉核验 |
欢迎加入 CPF-RN 鸿蒙社区:https://atomgit.com/CPF-RN
React Native for OpenHarmony 组织:https://atomgit.com/oh-react-native
RN 三方库鸿蒙适配清单:https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview
更多推荐



所有评论(0)