React Native for OpenHarmony 实战:三方库 react-native-google-places-autocomplete 的鸿蒙化适配指南
本文记录把 react-native-google-places-autocomplete(Google 地点自动补全输入框)适配到 HarmonyOS 的完整过程。
它做的事:一个输入框,边打字边向 Google Places API 请求候选地点,下拉展示列表。
结论:组件本体在鸿蒙上可以渲染;但"真实请求的返回结果"本轮无法验证 —— 因为 maps.googleapis.com 在本机请求超时、不可达。
不过这个库有一个非常好的特点:它把 qs 与 lodash.debounce 都自己实现了一遍(src/queryString.js / src/debounce.js)
⇒ 这两个纯函数模块给了我 13 条零网络、零平台依赖的判据,全部通过 ✓
另外本文要专门记两件事:一个"看起来致命、其实是误报"的预检结论,以及我连续第二轮把契约断言写错的复盘。
★★★★★ 一、先纠正一个误报:ListView 命中 14 处,但全是命名巧合
预检时我用 ListView 作关键词扫,命中 14 处 —— 按经验这几乎等于"用了 RN 0.61 已移除的组件 ⇒ import 即崩"。
但读进去后发现全是误报:
// GooglePlacesAutocomplete.js
listViewDisplayed: listViewDisplayedProp = 'auto', // ← 一个 prop 名
listViewDisplayedProp === 'auto' || listViewDisplayedProp === undefined,
const hideListView = useCallback(…) // ← 一个函数名
hideListView(); hideListView(true); // ← 调用那个函数
style={mergedStyles.listView} // ← 一个样式键名
非注释行里 12 处"命中",没有一处是真实的 ListView 使用:
- ❌ 没有
import {ListView} from 'react-native' - ❌ 没有
<ListView …> - ❌ 没有
new ListView.DataSource(…)
⇒ ⇒ 所以这个库不会因为 ListView 崩 ✓✓
⇒ 教训(值得写进预检规则):
用
ListView作关键词粗扫,会被listViewDisplayed/hideListView/styles.listView这类"名字里含 ListView"的标识符全部骗到。
正确的扫描目标应该是"真实使用形态":
ListView\.DataSource、import\s*\{[^}]*\bListView\b、<ListView、new ListView
(反例形态:如果真有 import {ListView} from 'react-native' + new ListView.DataSource(...) 写在模块顶层 ⇒ 那才是真的 import 即崩,只能否决。)
★★★★★ 二、13 条纯函数判据,全部通过(零网络)
这个库没有 dependencies —— 因为它把两个常用的第三方能力自己实现了:
| 文件 | 替代了什么 |
|---|---|
src/queryString.js(1297 B) | qs.stringify 的一个子集 |
src/debounce.js(683 B) | lodash.debounce |
⇒ 这两个是纯函数 ⇒ 可以脱离网络与平台直接断言 ✓✓
queryString.stringify —— 9 条全 PASS
PASS | ★ stringify({a:1}) = "a=1"
PASS | ★ 空格编码为 %20 → q=x%20y
PASS | ★★ RFC3986:"!*" 也要转义 → k=%21%2A
PASS | ★ undefined 被省略 → a=1
PASS | ★ null 序列化为 "key=" → k=
PASS | ★★ 数组用 indices 格式 → a%5B0%5D=x&a%5B1%5D=y
PASS | ★★ 嵌套对象用 bracket 格式 → a%5Bb%5D=x
PASS | ★ 空入参返回空串 → ''
PASS | ★★ 组合场景:key 里的 & 被编码 → key=K%261&input=a%20b&components%5Bcountry%5D=cn
其中两条特别值得看:
- RFC3986 编码:
encodeURIComponent不转义!'()*,而qs会转义 ⇒
这个实现里专门补了:
实测const encode = (value) => encodeURIComponent(String(value)).replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());{k: '!*'}⇒k=%21%2A✓ ⇒ 说明它连这个细节都对齐了qs✓ - 组合场景:
{key: 'K&1', input: 'a b', components: {country: 'cn'}}⇒
key=K%261&input=a%20b&components%5Bcountry%5D=cn✓
⇒&被正确编码成%26,否则会把一个参数拆成两个 —— 这是真实请求里最容易出错的地方 ✓
debounce —— 3 条全 PASS
PASS | ★ debounce 同步阶段尚未执行 → 0 次
PASS | ★★ debounce:5 次连续调用只执行 1 次 → 1 次
PASS | ★★ debounce.cancel() 有效(取消后不执行) → 0 次
⇒ 该实现是 trailing-edge(最后一次调用后 wait 毫秒才执行)+ 暴露 .cancel() ✓
⇒ wait = 0 也会延到下一个 tick(源码注释里说明这是为了对齐 lodash 的行为)✓
为什么这两个判据特别有价值:
它们完全不需要网络、不需要设备输入、不受平台差异影响 ⇒ 结论在任何基线上都成立 ✓✓
对一个"强依赖网络"的库来说,把可验证的部分切出来单独验证,是这个库唯一诚实的验证路径。
★★★★ 三、组件能渲染:placeholder 文本可读
<GooglePlacesAutocomplete
placeholder="GPA 搜索占位"
minLength={2}
requestUrl={{url: 'http://127.0.0.1:9/', useOnPlatform: 'all'}}
query={{key: 'TEST-KEY', language: 'zh', components: 'country:cn'}}
/>
实测(布局 dump 里找到):
✓ 找到 placeholder「GPA 搜索占位」 w=1188 h=42 x=66 y=962
⇒ 组件的输入框正常渲染、placeholder 正确显示 ✓
⇒ 而且 requestUrl 这个 prop 可以把请求指向自定义 URL(源码 getRequestUrl():
useOnPlatform === 'all' 时直接用 requestUrl.url)⇒ 这是给"网络不可用"环境留的出口 ✓
★★★ 四、网络不可达 —— 我如何处理这个验证边界
预检时就确认了:
maps.googleapis.com 不可达:操作超时
而库里的 URL 是硬编码的:
const GOOGLE_API_BASE = 'https://maps.googleapis.com/maps/api';
⇒ ⇒ 所以在当前环境下,"输入 → 请求 → 拿到候选列表"这条主链路无法验证 ✗
我的处理:
- 把能验证的都验证透(纯函数 13 条 + 组件渲染)✓
- 把
requestUrl指到http://127.0.0.1:9/(一个必然ECONNREFUSED的地址)⇒
设计意图是:只要请求真的发出去,onFail就该被回调 ⇒ 用它证明"请求链路是通的、错误处理也正常" - 实测结果:
onFail回调次数 = 0 ⚠️
⇒ 原因很清楚:这个库只在"用户输入且长度 ≥minLength"之后才发请求,
而本轮没有在输入框里打字(我全程用代码驱动,没做输入注入)⇒ 请求根本没发 ✗
⇒ ⇒ 所以"XHR 失败路径"这一条本轮未验证,如实记录 ✓
⚠️ 这也说明了一个取舍:用
requestUrl造"必然失败"是个好思路,但它仍然需要"触发输入"这一步。
而 RNOH 上的输入注入(uitest uiInput inputText)本身不可靠(实测过),
所以我这次没有把它算进必做项 —— 宁可诚实地少一条判据,也不写"看起来验证了"的结论 ✓
★★★★★ 五、我连续第二轮把契约断言写错(必须记下来)
契约部分 3 条 FAIL,全是我的问题:
NOTE | 具名导出 GooglePlacesAutocomplete 的 typeof = object
FAIL | ★ 具名导出是函数(forwardRef 组件) | 期望=function | 实际=object
NOTE | $$typeof = Symbol(react.forward_ref)
FAIL | ★★ 是 forwardRef($$typeof = react.forward_ref) | 期望=Symbol(react.forward_ref) | 实际=undefined
FAIL | ★★ 对象里有 GooglePlacesAutocomplete 键 | 期望=function | 实际=object
两个根因:
根因一:forwardRef 返回的是对象,不是函数
源码:
export const GooglePlacesAutocomplete = forwardRef((props, ref) => { … });
export default { GooglePlacesAutocomplete };
⇒ forwardRef(render) 的返回值是一个带 $$typeof: Symbol(react.forward_ref) 的对象,typeof 就是 "object" ✓
⇒ 所以"具名导出是 function"这条断言本身就是错的 ✗
(同一个坑之前也踩过 —— 那时也误判成 function)
正确的断言写法:
const isForwardRef = (v) =>
v != null && typeof v === 'object' && String(v.$$typeof) === 'Symbol(react.forward_ref)';
根因二:我的 S() 工具函数序列化不了 Symbol
const S = (v) => { try { return JSON.stringify(v); } catch { return String(v); } };
// JSON.stringify(Symbol('x')) ⇒ undefined(不抛错,直接返回 undefined)
⇒ 于是 $$typeof 断言拿到的是 'undefined',看起来像"没有 $$typeof",其实是我工具的问题 ✗
修法:序列化前先判 Symbol:
if (typeof v === 'symbol') return String(v);
复盘:这是连续第二轮在契约断言上写错
此前某一轮错在凭"常见默认值"猜(duration / hideStatusBar / ColorTheme 类型),
这一轮错在凭"组件就该是函数"猜(forwardRef 的实际形态)+ 判据工具缺陷。
⇒ 共同点是"预期值不是从源码/实测来的" ✓
⇒ 系统性的改法应该是:写断言前先把源码里的"形态事实"抄下来(导出语句、typeof、$$typeof),
而不是先写断言、再等它 FAIL 来纠正 ✓
★★ 六、其余预检结论
| 预检项 | 结果 |
|---|---|
dependencies | ✅ 空(qs / lodash.debounce 都自己实现了) |
peerDependencies | ✅ 只有 react >= 16.8 / react-native >= 0.59 ⇒ 无原生 peer |
ListView | ⚠️ 14 处命中,但全是命名巧合(见第一节)⇒ 真实使用为 0 ✓ |
老写法九项(createClass / React.PropTypes / ViewPropTypes / Text.propTypes / 字符串 ref / this.refs / mixins / componentWillReceiveProps / componentWillMount) | ✅✅ 全部为空 |
isRequired | ✅ 空 |
Platform.OS | 4 处,都是 'web' / 'android' 判断 ⇒ 鸿蒙走非 web、非 android 分支 ✓ |
measure() / onLayout / useNativeDriver / NativeModules | ✅ 全空 |
| 网络请求 | 3 处 new XMLHttpRequest()(搜索、第二路搜索、详情)⇒ 真实请求 |
scripts.prepare | ⚠️ husky install ⇒ 安装必须 --ignore-scripts |
| 交付包形态 | ✅ GooglePlacesAutocomplete.js(32344 B)+ src/ 6 个模块 + index.d.ts(10898 B) |
| 静态资源 | ✅ 自带 images/powered_by_google_on_white*.png(3 种倍图) |




七、接入与预检(要点)
# ★ 必须 --ignore-scripts:它的 scripts.prepare 是 "husky install"
npm install "git+https://atomgit.com/oh-react-native/react-native-google-places-autocomplete.git#2.6.5-ohos-1.0.0" --ignore-scripts
# 它没有 dependencies ⇒ 不需要补装任何东西
八、交付包质量与基线说明
| 类别 | 结果 |
|---|---|
| 交付包内容 | GooglePlacesAutocomplete.js(32344 B)+ src/(debounce.js / queryString.js / results.js / Row.js / styles.js / uuid.js)+ GooglePlacesAutocomplete.d.ts(10898 B)+ 3 张品牌图 ⇒ 完整 ✓(20 文件) |
spec.json | ⚠️ 缺 upstreamCommit |
validation | ⚠️ "build-pass-device-pending" |
| ⚠️ 声明的基线 | reactNative: "0.88.0" + rnoh: "0.84.4" ⇒ 两字段自相矛盾 |
| 备注 | 库代码零改动(本轮未修改库任何文件) |
关于基线:本次验证在 RN 0.84.1 + RNOH 0.84.3 上完成。
纯函数判据(13 条)与平台无关 ⇒ 结论跨基线成立;组件渲染结论也跨基线成立。
唯一与基线相关的是"网络"(与环境有关,不是与基线有关)。
九、已知限制与本次验证边界
9.1 用这个库要知道的
| 事项 | 说明 |
|---|---|
★★★★★ maps.googleapis.com 必须可达 | 这是它的硬依赖;本机不可达 ⇒ 主链路无法验证 |
★★★★★ 它自己实现了 qs 与 debounce | src/queryString.js / src/debounce.js ⇒ dependencies 为空,且这两个纯函数可单独验证 |
★★★★ requestUrl prop 可以把请求指向自定义 URL | useOnPlatform: 'all' 时直接用 requestUrl.url ⇒ 便于本地 mock |
★★★ GooglePlacesAutocomplete 是 forwardRef | typeof 是 object、$$typeof 是 Symbol(react.forward_ref) ⇒ 断言时别当函数 |
| ★★★ 默认导出是【对象】 | export default { GooglePlacesAutocomplete } ⇒ import X from … 拿到的是对象 |
★★ ListView 关键词会误报 | listViewDisplayed / hideListView / styles.listView 都不是那个已移除的组件 |
| ★★ 请求只在输入后触发 | 且要求长度 ≥ minLength(默认 2)⇒ 不输入就永远不会发请求 |
★ scripts.prepare 会跑 husky install | 安装必须 --ignore-scripts |
9.2 本次验证的边界
- ★★★ 未验证"真实请求 → 候选列表"主链路(
maps.googleapis.com不可达); - ★★★ 未验证 XHR 失败路径的
onFail回调(onFail实测 0 次 —— 因为没触发输入,请求没发出); - ★★ 未验证
fetchDetails的详情二次请求; - ★★ 未验证下拉列表的渲染与点击选择(依赖真实请求结果);
- ★★ 未验证
debounceprop 与真实输入的组合行为(只单独验证了debounce函数本身); - 未验证
predefinedPlaces/currentLocation/enableHighAccuracyLocation; - 未验证
styles/renderRow/renderDescription/renderLeftButton等自定义渲染; - 未验证
GooglePlacesDetailsQuery与fetchDetails组合; - 未在
RN 0.88.0/RNOH 0.84.4上复验; - 未测其它屏幕宽度;未做跨平台对照。
库代码未改动。
十、常见问题
Q1:它在 RN 0.84 上能跑吗?
组件能渲染(placeholder 正确显示在 y=962),纯函数全部正确(queryString.stringify 9 条 + debounce 3 条)。
但"真实请求 → 候选列表"这条主链路本轮无法验证,因为 maps.googleapis.com 在本机请求超时、不可达。
Q2:ListView 命中是不是意味着它崩?
不一定 —— 这个库的 14 处"命中"全是 listViewDisplayed(prop)、hideListView(函数)、styles.listView(样式键)。
判据要看真实使用形态:ListView.DataSource、import {…ListView…}、<ListView、new ListView。
(如果真有 new ListView.DataSource(...) 写在模块顶层,那才是 import 即崩。)
Q3:没有 Google 网络怎么测?
两条路:
- 切出纯函数单独测(这个库做得很好:
src/queryString.js/src/debounce.js都能直接 import 断言); - 用
requestUrl指向自己的 mock 服务:
⚠️ 但要注意:请求只在"输入长度 ≥<GooglePlacesAutocomplete requestUrl={{url: 'http://你的mock地址', useOnPlatform: 'all'}} query={{key: 'x'}} />minLength"之后才发 ⇒ 还必须先真的输入文字。
Q4:onFail 一直不触发?
因为它只在请求失败时回调;而没有输入就不会发请求。
本轮的实测就是 0 次 —— 我把 requestUrl 指到了必然拒绝的 127.0.0.1:9,但因为没触发输入,请求根本没发出。
Q5:怎么判断这个导出到底是组件还是对象?
import GpaDefault, {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete';
typeof GooglePlacesAutocomplete; // ★ 'object'(它是 forwardRef 的结果)
String(GooglePlacesAutocomplete.$$typeof); // ★ 'Symbol(react.forward_ref)'
typeof GpaDefault; // ★ 'object'(默认导出是个对象)
typeof GpaDefault.GooglePlacesAutocomplete; // ★ 'object'
⇒ 别用 typeof === 'function' 判断 forwardRef 组件 ✓
Q6:queryString.stringify 的行为和 qs 一致吗?
大部分一致:undefined 省略、null ⇒ key=、数组 ⇒ key[0]=、嵌套 ⇒ key[sub]=、
RFC3986 编码(!'()* 也转义)。
差异(本轮未逐一验证):qs 还支持 arrayFormat 选项、深度限制、allowDots 等,这个实现只覆盖了"本库用到的子集"(文件头注释里也这么写)。
小结
react-native-google-places-autocomplete 是Google 地点自动补全输入框,
代码本身很现代(forwardRef + hooks)、老写法九项全空、dependencies 为空。
最值得记下来的是这几条:
-
★★★★★
ListView的 14 处命中是纯误报,教训在"扫描目标"。
全是listViewDisplayed/hideListView/styles.listView。
⇒ 扫ListView这个词会骗人,要扫真实使用形态:ListView.DataSource、import {…ListView…}、<ListView✓
⇒ 这条规则能避免"看到命中就下否决结论"的错误(也避免了误丢掉一个可用的库)。 -
★★★★★ 13 条纯函数判据全部通过,是这个库最诚实的验证路径。
因为它自己实现了qs.stringify子集与lodash.debounce
⇒ 两个纯函数可以脱离网络与平台直接断言(含 RFC3986 的!'()*转义、&编码、数组/嵌套格式、debounce 合并与cancel)✓✓
⇒ 对强依赖网络的库,"把可验证的部分切出来"比"想办法造网络"更划算。 -
★★★★★ 我连续第二轮把契约断言写错 —— 根因是"预期值不是从源码来的"。
这轮错在:forwardRef返回对象(我当函数)、以及我的S()序列化不了 Symbol(把$$typeof变成'undefined')。
上轮错在:凭"常见默认值"猜duration/hideStatusBar/ColorTheme类型。
⇒ 系统性的改法:写断言前,先把源码里的"形态事实"抄下来(导出语句、typeof、$$typeof、常量块),
别先写断言、再等它 FAIL 来纠正 ✓ -
★★★ 网络不可达时的诚实处理。
预检就把maps.googleapis.com探了一遍(超时)⇒ 不写"验证了主链路";
也不把"用必然失败的 URL 触发onFail"硬算成已验证(因为还需要触发输入,而输入注入不可靠)⇒
宁可诚实地少一条判据 ✓ -
★★★ 组件渲染这一条仍然值得做:
placeholder文本可读(y=962)⇒ 至少证明"它能在这个平台上挂载并渲染" ✓ -
★★
requestUrl这类"可替换 URL"的 prop 是网络库的宝贵出口。
它让"依赖外网"变成"依赖一个可替换的端点" ⇒ 有条件时可以指向本地 mock ✓
⚠️ 但别忘了它还需要"真实输入"才会发请求。 -
★★ 默认导出是对象而不是组件(
export default { GooglePlacesAutocomplete })⇒
import X from …拿到的是对象、X.GooglePlacesAutocomplete才是那个forwardRef对象 ⇒
导入方式与断言方式都要照着源码写 ✓
本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-google-places-autocomplete(上游 2.6.5 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-google-places-autocomplete |
| 适配 TAG | 2.6.5-ohos-1.0.0 |
| 需要 HAR / 权限 / ohpm | 都不需要(纯 JS) |
| 库类型 | 地点自动补全输入框(依赖 Google Places API) |
| 运行时依赖 | ✅ dependencies 为空(qs / lodash.debounce 都是自己实现的) |
| 导出形态 | export const GooglePlacesAutocomplete = forwardRef(...) ⇒ typeof 为 object;默认导出是对象 {GooglePlacesAutocomplete} |
| ⚠️ 外部硬依赖 | https://maps.googleapis.com/maps/api 必须可达 |
⚠️ scripts.prepare | husky install ⇒ 安装需 --ignore-scripts |
| 许可证 | MIT |
| 上游仓库 | https://github.com/FaridSafi/react-native-google-places-autocomplete |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = GooglePlacesTestApp) |
npm install "git+https://atomgit.com/oh-react-native/react-native-google-places-autocomplete.git#2.6.5-ohos-1.0.0" --ignore-scripts
import GpaDefault, {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete';
// ① 正常用法(需要可访问 Google Places API)
<GooglePlacesAutocomplete
placeholder="搜索地点"
minLength={2}
fetchDetails={false}
query={{key: 'YOUR_KEY', language: 'zh', components: 'country:cn'}}
onPress={(data, details = null) => console.log(data.description)}
/>
// ② 指向自己的 mock 端点(无外网时)
<GooglePlacesAutocomplete
requestUrl={{url: 'http://127.0.0.1:8080/places', useOnPlatform: 'all'}}
query={{key: 'x'}}
/>
// ③ 单独用它的纯函数(无需网络,最稳的验证路径)
import {stringify} from 'react-native-google-places-autocomplete/src/queryString';
import {debounce} from 'react-native-google-places-autocomplete/src/debounce';
stringify({a: 1, b: 'x y'}); // 'a=1&b=x%20y'
stringify({k: '!*'}); // 'k=%21%2A'(RFC3986)
debounce(fn, 120)(); debounce(fn, 120).cancel?.();
// ⚠️ GooglePlacesAutocomplete 是 forwardRef ⇒ typeof 是 'object',不是 'function'
// ⚠️ 默认导出是对象 {GooglePlacesAutocomplete},不是组件
// ⚠️ 请求只在输入长度 ≥ minLength 后才发 ⇒ 不输入永远不会发请求、onFail 也不会触发
// ⚠️ 安装必须 --ignore-scripts(prepare = husky install)
# 换页启动测试页 —— force-stop 不能省
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey GooglePlacesTestApp
判据的复现方式
# ① 纯函数 + 契约:逐条 PASS/FAIL
hdc shell "hilog -x | grep gpa-test" | Select-String 'PASS|FAIL|SUMMARY|NOTE'
# ⇒ queryString 9 条 + debounce 3 条全 PASS;3 条 FAIL 是我自己的断言写错
# ② 组件渲染:placeholder 文本
devecocli ui layout | Select-String 'GPA 搜索占位'
# ③ 网络可达性(预检就该做)
# Invoke-WebRequest https://maps.googleapis.com/... ⇒ 本机超时
验证环境
| 项 | 版本 |
|---|---|
| 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 / npm | v24.14.0 / 11.9.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),1320×2232,density 3 |
| 本机状态栏高度 | 117px = 39vp |
| 宿主依赖 | ✅ 无需额外依赖(dependencies 为空) |
| 宿主 HAP 产物 | entry-default-signed.hap |
| 本次构建 | assembleHap 3 分 43 秒 ⇒ 一次成功(打包与构建退出码都检查了) |
| 验证规模 | 纯函数判据 13/13 全 PASS(queryString 9 + debounce 3 + 同步阶段 1);组件渲染成功(placeholder 可见);契约 3 条 FAIL 全是我自己写错;onFail 0 次(未触发输入,故失败路径未验证);LogBox 无通知;4 张截图互异 |
⚠️ 基线说明:交付包
spec.json声明RN 0.88.0 + RNOH 0.84.4(两字段自相矛盾),本次验证在RN 0.84.1 + RNOH 0.84.3上完成。
纯函数判据与平台无关 ⇒ 结论跨基线成立;网络相关结论取决于运行环境,与基线无关。
相关社区与项目地址
| 项目 | 地址 |
|---|---|
| React Native 鸿蒙官方社区(CPF-RN) | https://atomgit.com/CPF-RN |
| RNOH 框架仓库 | https://atomgit.com/openharmony-RN/ohos_react_native |
| RNOH 三方库使用文档 | https://atomgit.com/OpenHarmony-RN/usage-docs |
| React Native for OpenHarmony 组织 | https://atomgit.com/oh-react-native |
| RN 三方库鸿蒙适配清单 | https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview |
欢迎加入 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)