React Native for OpenHarmony 实战:三方库 react-native-tag-input 的鸿蒙化适配指南
本文记录把 react-native-tag-input(标签输入框)适配到 HarmonyOS 的完整过程。
它做的事:把一组标签(tag)横向排列,末尾跟一个 TextInput 用于输入;
每个标签带一个 × 可点删除,Backspace 键在输入框为空时删掉最后一个标签。
结论:可用,而且是零 FAIL —— 23 条判据全 PASS。
这个库是我遇到的**“预检三个风险点、但每个都能提前判定为安全”**的典型:
它用了 ViewPropTypes.style 和 Text.propTypes.style(RN 0.44+ 已移除)、
写了 invariant(Platform.OS === 'ios', …)、还 import 了 RN 内部路径。
但逐条读上下文之后,这三个都能在动手之前判定为"不会出问题" ——
本文第一节就是这三条判定过程。
★★★★★ 一、三个风险点,逐条读上下文都判定为安全
预检扫描出三个会让人"心里一沉"的命中。但每一条读完上下文都排除了:
① ViewPropTypes.style / Text.propTypes.style(各 2 处)—— 需要 polyfill
// index.js L117-L118
tagContainerStyle: ViewPropTypes.style,
tagTextStyle: Text.propTypes.style,
(L380-L381 在 Tag 组件里同样有两处。)
判定:ViewPropTypes 与 Text.propTypes 在 RN 0.44+ 已被移除,
但工程里已经有一份 polyfill(polyfills/text-prop-types.js,在 index.js 第 28 行导入),
它同时补了 ViewPropTypes(Object.defineProperty getter 形式)与 Text.propTypes。
⇒ 不需要新增任何东西,直接复用 ✓
实测结果:页面正常渲染、LogBox 无通知 ✓(后面第三节有证据)
② invariant(Platform.OS === "ios", …) —— 永远不会被执行
// index.js L209-L212
onBlur = (event) => {
invariant(Platform.OS === "ios", "only iOS gets text on TextInput.onBlur");
this.props.onChangeText(event.nativeEvent.text);
}
第一眼:鸿蒙上 Platform.OS !== 'ios' ⇒ invariant 会抛错 ⇒ 崩。
但看它的用法(L295):
onBlur={Platform.OS === "ios" ? this.onBlur : undefined}
⇒ ⇒ 鸿蒙上 Platform.OS !== 'ios',所以传给 TextInput 的是 undefined
⇒ ⇒ this.onBlur 永远不会被调用 ⇒ invariant 永远不会执行 ✓✓
判断要点:"代码里有 invariant(x)“不等于"它会执行” ——
要先看包含这个 invariant 的函数,在什么条件下才会被调用。
这里是同一个文件里两行的互相配合(L295 做了平台判断,L209 的 invariant 是对"这个 prop 只该在 iOS 传"的断言)。
③ import type { StyleObj } from 'react-native/Libraries/StyleSheet/StyleSheetTypes' —— 会被 Babel 消除
// index.js L1-L6
// @flow
import type {
StyleObj,
} from 'react-native/Libraries/StyleSheet/StyleSheetTypes';
第一眼:这是 RN 的内部路径,Metro 很可能解析失败。
但它是 import type —— Flow 的类型导入,在转译时会被整体消除(不产生任何 require)。
⇒ Metro 根本看不到这条路径 ✓✓
实测结果:打包 bundle exit=0、构建 BUILD SUCCESSFUL ✓(后面第四节有记录)
三条判定的共同点
| 风险点 | 表面现象 | 实际 |
|---|---|---|
ViewPropTypes / Text.propTypes | 老 API,已移除 | 有现成 polyfill |
invariant(Platform.OS === 'ios') | 鸿蒙上会抛错 | 该函数永不调用(调用处做了平台判断) |
react-native/Libraries/... 内部路径 | Metro 会解析失败 | import type ⇒ 转译时消除 |
⇒ ⇒ 这类"预检命中"要先读上下文再决定要不要改代码 ——
如果一看到 ViewPropTypes 就动手改库,反而会引入不必要的改动。
本库代码最终零改动。
★★★★★ 二、23 条判据全 PASS —— SUMMARY | 合计 23/23 通过,失败 0
契约(17 条)
PASS | ★★★ TagInput 是 class(function)
PASS | ★★★ 有 prototype.render(它是 class 组件)
NOTE | TagInput.prototype.isPureReactComponent = true
PASS | ★★★★ 它是 PureComponent(isPureReactComponent === true)
PASS | ★★★★★ propTypes 清单(16 项)
PASS | ★★★★★ defaultProps 清单(6 项)
PASS | ★★★ defaultProps.editable = true
PASS | ★★★ defaultProps.tagColor = '#dddddd'
PASS | ★★★ defaultProps.tagTextColor = '#777777'
PASS | ★★★ defaultProps.inputDefaultWidth = 90
PASS | ★★★ defaultProps.inputColor = '#777777'
PASS | ★★★ defaultProps.maxHeight = 75
PASS | ★★★ 3 个方法在 prototype 上(render / cWRP / cWU)
PASS | ★★★★ 箭头类字段【不在】prototype 上(focus/removeIndex/scrollToEnd/onKeyPress/measureWrapper)
propTypes 16 项(逐个比对):
value / onChange / labelExtractor / text / onChangeText / editable /
tagColor / tagTextColor / tagContainerStyle / tagTextStyle /
inputDefaultWidth / inputColor / inputProps / maxHeight /
onHeightChange / scrollViewProps
(前 5 项 value / onChange / labelExtractor / text / onChangeText 是 isRequired)
defaultProps 6 项(清单 + 精确值):
| 键 | 值 |
|---|---|
editable | true |
tagColor | '#dddddd' |
tagTextColor | '#777777' |
inputDefaultWidth | 90 |
inputColor | '#777777' |
maxHeight | 75 |
★★★★ PureComponent 的精确判据
class TagInput<T> extends React.PureComponent<…> // index.js L106
⇒ TagInput.prototype.isPureReactComponent === true ✓
NOTE | TagInput.prototype.isPureReactComponent = true
PASS | ★★★★ 它是 PureComponent(isPureReactComponent === true)
⇒ 这条判据比"typeof 是 function"精确得多 —— 它区分了 Component 与 PureComponent ✓
★★★★ 方法归属:坑里的"三档规则"这次三档齐全
同一个类里,方法有三种归属方式,实测结果:
| 归属方式 | 本库的例子 | 实测 |
|---|---|---|
原型方法(xxx() {}) | render / componentWillReceiveProps / componentWillUpdate | 在 prototype 上 ✓ |
箭头类字段(xxx = () => {}) | measureWrapper / onBlur / onKeyPress / focus / removeIndex / scrollToEnd / tagInputRef / scrollViewRef / onScrollViewContentSizeChange / onLayoutLastTag | 只在实例上,不在 prototype ✓ |
| constructor 内绑定 | (本库没有) | — |
实测(两条方向相反的断言都过):
NOTE | prototype 上的成员 = componentWillReceiveProps, componentWillUpdate, render
PASS | ★★★ 3 个方法在 prototype 上(render / cWRP / cWU)
NOTE | 箭头字段里错误地出现在 prototype 上的 = (无)
PASS | ★★★★ 箭头类字段【不在】prototype 上
NOTE | 实例是否可得 = true
PASS | ★★★★ 5 个箭头字段在【实例】上都存在 | 期望=5 | 实际=5
PASS | ★★★★ 而 prototype 上没有 focus
PASS | ★★★★ 而 prototype 上没有 removeIndex
PASS | ★★★★ 而 prototype 上没有 scrollToEnd
⇒ ⇒ 同一组方法:「实例上有」+「prototype 上没有」两条都成立,才是完整证据 ✓✓
⇒ 如果只断言"实例上有",那么"它其实也在 prototype 上"这种情况就漏掉了。
★★★★ 三、static inputWidth 纯函数的 4 条判据(含边界)
源码(index.js L145-L158):
static inputWidth(text, spaceLeft, inputDefaultWidth, wrapperWidth) {
if (text === "") {
return inputDefaultWidth;
} else if (spaceLeft >= 100) {
return spaceLeft - 10;
} else {
return wrapperWidth;
}
}
实测 4 条全 PASS:
| # | 调用 | 期望(手推) | 实测 |
|---|---|---|---|
| 1 | inputWidth("", 0, 90, 1320) | 90(走第一分支) | ✅ |
| 2 | inputWidth("abc", 150, 90, 1320) | 140(spaceLeft − 10) | ✅ |
| 3 | inputWidth("abc", 50, 90, 1320) | 1320(走 wrapperWidth) | ✅ |
| 4 | inputWidth("abc", 100, 90, 1320) | 90(边界 100 满足 >= 100,100 − 10 = 90,不是 wrapperWidth) | ✅ |
⇒ 第 4 条最有价值:spaceLeft = 100 时,100 − 10 = 90 恰好等于 inputDefaultWidth = 90
—— 如果只测第 2、3 条,看不出这个"两个分支给出相同数字"的巧合;
边界值单独测一次,才知道它走的是哪个分支 ✓
★★★★★ 四、渲染证据:3 处标签文本 + 2 个 TextInput
页面里放了两个 TagInput:E1 有 2 个标签、E2 有 1 个标签。
布局 dump 实测:
✓ [Text] 「TGI 标签一 ×」 w=236 h=49 x=78 y=1244
✓ [Text] 「TGI 标签二 ×」 w=237 h=49 x=371 y=1244
✓ [Text] 「TGI 单个标签 ×」 w=278 h=49 x=78 y=1536
TextInput#TextInput(68) [639,1215,909,1269] clickable longClickable scrollable
TextInput#TextInput(94) [389,1507,809,1561] clickable longClickable scrollable
从这些数字能读出四件事 ✓✓:
| 观察 | 结论 |
|---|---|
TGI 标签一 × 与 TGI 标签二 × 的 y 都是 1244 | 两个标签在同一行(横向排列)✓ |
x = 78 与 371 | 第二个标签在第一个右边(371 − 78 = 293)⇒ 横向排布 ✓ |
标签文本末尾都带 × | 库自动加的删除符号(见下) |
两个 TextInput 都在标签之后 | x = 639(E1,两个标签之后)、x = 389(E2,一个标签之后)⇒ 输入框紧跟最后一个标签 ✓ |
× 是从哪来的?—— 回源码找到了根因
dump 里看到标签文本带 ×,于是回去看 Tag 的 render()(index.js L396-410):
tagLabel = (
<Text style={[styles.tagText, {color: this.props.tagTextColor}, this.props.tagTextStyle]}>
{this.props.label}
× {/* ← ★ JSX 实体 × = × */}
</Text>
);
⇒ ⇒ 这个 × 是库用 JSX 实体 × 硬编码进去的,不可配置 ✓
⇒ ⇒ 所以"标签文本里带 ×"不是我的数据带进来的,是库的行为 ✓
这一步的方法论:dump 里出现"预期之外的字符"时,不要急着怀疑自己的数据 ——
回源码找那个字符串的来源。这次找到的是 JSX 实体×(而不是"×"字面量),
说明作者是有意用 HTML 实体写的,也说明无法通过 prop 去掉它。
⚠️ placeholder="Start typing" 没有出现在 dump 里 ——
placeholder 是 TextInput 的属性而不是布局文本节点,ui layout 不显示它。
⇒ 这与"accessibilityLabel 不进 dump"是同一类现象 ⇒ 如实记"未观察到" ✓
★★ 五、其余预检结论
| 预检项 | 结果 |
|---|---|
dependencies | invariant ^2.2.2 + prop-types ^15.5.10(prop-types 宿主已有,invariant 需装) |
peerDependencies | ✅ 只有 react / react-native |
createClass | ✅ 精确命中 0、裸词也 0 |
this.refs / mixins / getDefaultProps / ListView / measure() | ✅ 全部空 |
componentWillReceiveProps 2 处 | ⚠️ React 19 已移除 ⇒ 不会执行(外部改 value 时的内部同步逻辑会失效,但本页由父组件驱动,无影响) |
TextInput 15 处 | ✓ 核心组件 |
onLayout 9 处 | ✓ 用于测量标签换行位置 |
TouchableOpacity / TouchableWithoutFeedback | ✓(点击标签删除、点空白区域聚焦输入框) |




六、接入与预检(要点)
npm install "git+https://atomgit.com/oh-react-native/react-native-tag-input.git#0.0.21-ohos-1.0.0" --ignore-scripts
# 依赖:invariant(需装)+ prop-types(宿主已有)
# 还需要 ViewPropTypes / Text.propTypes 的 polyfill(工程里已有)
七、交付包质量与基线说明
| 类别 | 结果 |
|---|---|
| 交付包内容 | 单文件 index.js(13437 B / 487 行),10 个文件 |
| ⚠️ 老 API | ViewPropTypes.style ×2、Text.propTypes.style ×2 ⇒ 需 polyfill |
| ⚠️ 平台断言 | invariant(Platform.OS === "ios", …) ⇒ 但永不执行(调用处做了判断) |
| ⚠️ 内部路径 | import type … from 'react-native/Libraries/…' ⇒ import type 会被消除 |
| ⚠️ 生命周期 | componentWillReceiveProps ×2 ⇒ React 19 已移除,不执行 |
spec.json | ⚠️ 缺 upstreamCommit |
validation | ⚠️ "build-pass-device-pending" |
| ⚠️ 声明的基线 | reactNative: "0.88.0" + rnoh: "0.84.4" ⇒ 两字段自相矛盾 |
| 备注 | 库代码零改动 |
关于基线:本次验证在 RN 0.84.1 + RNOH 0.84.3 上完成。
本库零原生代码、平台差异只体现在 Platform.OS 的两处(且都不影响鸿蒙)⇒ 结论跨基线成立 ✓
八、已知限制与本次验证边界
8.1 用这个库要知道的
| 事项 | 说明 |
|---|---|
★★★★★ TagInput 是 PureComponent | prototype.isPureReactComponent === true |
★★★★★ propTypes 16 项、defaultProps 6 项 | 前 5 项 isRequired;默认值 '#dddddd' / '#777777' / 90 / 75 |
| ★★★★★ 方法归属分两档 | render/cWRP/cWU 在 prototype;focus/removeIndex/scrollToEnd 等 10 个箭头字段只在实例上 |
★★★★ × 是硬编码的 | Tag 的 render() 里写了 × ⇒ 无法通过 prop 去掉 |
★★★★ static inputWidth 是纯函数 | text === '' ⇒ inputDefaultWidth;spaceLeft >= 100 ⇒ spaceLeft − 10;否则 wrapperWidth |
★★★ 需要 ViewPropTypes / Text.propTypes polyfill | RN 0.44+ 已移除,本库仍在用 |
★★★ invariant(Platform.OS === 'ios') 不会在鸿蒙执行 | 调用处 onBlur={Platform.OS === 'ios' ? this.onBlur : undefined} |
★★ componentWillReceiveProps 不执行 | React 19 已移除 ⇒ 外部改 value 时的内部同步失效 |
★★ placeholder 不会出现在布局 dump 里 | 它是属性而不是布局文本节点 |
★★ 依赖 invariant | 需要装(prop-types 宿主已有) |
8.2 本次验证的边界
- ★★★ 未验证交互(点
×删标签、Backspace删最后一个、点空白聚焦——onChange/onKeyPress未被真实触摸/按键触发); - ★★★ 未验证标签自动换行(
maxHeight: 75的截断与ScrollView滚动); - ★★ 未验证
onHeightChange/scrollViewProps/inputProps等透传 prop; - ★★ 未验证
componentWillReceiveProps缺失带来的实际影响(外部改value时的行为); - 未验证
Tag的onPress删除是否真的把标签移除了; - 未在
RN 0.88.0/RNOH 0.84.4上复验; - 未测其它屏幕宽度(
windowWidth在模块加载时取自Dimensions.get('window'),不会随旋转更新); - 未做跨平台对照。
库代码未改动。
九、常见问题
Q1:它在 RN 0.84 上能跑吗?
能,零 FAIL。 23 条判据全 PASS:PureComponent 判定、propTypes 16 项、defaultProps 6 项精确值、
方法归属两档、inputWidth 4 条、3 处标签文本与 2 个 TextInput 都渲染出来;LogBox 无通知。
Q2:它用了 ViewPropTypes,会不会崩?
会崩 —— 但有 polyfill 就不崩。 工程里已有 polyfills/text-prop-types.js(在 index.js 第 28 行导入),
它同时补了 ViewPropTypes 与 Text.propTypes ⇒ 直接复用即可 ✓
Q3:invariant(Platform.OS === 'ios', …) 在鸿蒙上会抛错吗?
不会,因为它永远不会被调用:
onBlur={Platform.OS === "ios" ? this.onBlur : undefined} // 鸿蒙上传 undefined
onBlur = (event) => {
invariant(Platform.OS === "ios", "…"); // ← 永不到达
}
⇒ 判断"某个断言会不会执行",要看它的调用条件,而不是看它写在哪 ✓
Q4:它 import 了 react-native/Libraries/... 内部路径,Metro 能过吗?
能,因为那是 import type(Flow 类型导入),转译时被整体消除,不产生 require ✓
Q5:标签后面的 × 能去掉吗?
不能(通过 prop)。它在 Tag 的 render() 里被硬编码:
<Text>{this.props.label} ×</Text>
⇒ 想改只能 fork 这个库,或者用 labelExtractor 返回一个 React 元素
(源码里有分支:React.isValidElement(this.props.label) 为真时直接渲染、不加 ×)✓ ——
这是"想去掉 ×"的正规做法。
Q6:为什么 focus() / removeIndex() 在 prototype 上找不到?
因为它们是箭头类字段(focus = () => {...}),在构造时绑定到实例上,
不在 prototype 上。要用 ref 拿实例再调:
const ref = useRef(null);
<TagInput ref={ref} … />
ref.current.focus();
ref.current.removeIndex(0);
ref.current.scrollToEnd();
实测:实例上 5 个箭头字段都在、prototype 上都没有 ✓
Q7:inputWidth 是怎么算的?
static inputWidth(text, spaceLeft, inputDefaultWidth, wrapperWidth) {
if (text === "") return inputDefaultWidth; // 空输入 ⇒ 默认宽度
else if (spaceLeft >= 100) return spaceLeft - 10; // 剩余够 ⇒ 占剩余减 10
else return wrapperWidth; // 剩余不够 ⇒ 换行占满
}
⇒ 实测 4 条(含边界 spaceLeft = 100)全 PASS ✓
Q8:placeholder 为什么在 dump 里看不到?
因为 placeholder 是 TextInput 的属性,不是布局文本节点,ui layout 不显示它。
⇒ 想看它得用别的工具(比如无障碍树),布局 dump 只能看到真正渲染的 Text ✓
小结
react-native-tag-input 是标签输入框(标签 + × 删除 + 末尾 TextInput),
单文件 487 行、零原生代码、23 条判据全 PASS、零 FAIL。
最值得记下来的是这几条:
-
★★★★★ 三个"预检命中"都能在动手前判定为安全 —— 关键是读上下文。
ViewPropTypes.style/Text.propTypes.style⇒ 工程里已有 polyfill ✓invariant(Platform.OS === "ios", …)⇒ 调用处onBlur={Platform.OS === 'ios' ? … : undefined},该函数永不执行 ✓import … from 'react-native/Libraries/…'⇒ 是import type,转译时被消除 ✓
⇒ 教训:看到"老 API / 平台断言 / 内部路径"先别改代码 ——
先找它会不会被执行 / 会不会被解析。本库代码最终零改动。
-
★★★★★ 方法归属要"两个方向都断言"才是完整证据。
PASS | 3 个方法在 prototype 上(render / cWRP / cWU) PASS | 箭头类字段【不在】prototype 上 PASS | 5 个箭头字段在【实例】上都存在 PASS | 而 prototype 上没有 focus / removeIndex / scrollToEnd⇒ 只断言"实例上有"会漏掉"它其实也在 prototype 上";两个方向都查才完整 ✓
-
★★★★★
PureComponent有精确判据:prototype.isPureReactComponent === true。
⇒ 比"typeof是 function"精确得多 —— 它把Component与PureComponent区分开了 ✓ -
★★★★
inputWidth的边界值最有价值:
spaceLeft = 100时100 − 10 = 90,恰好等于inputDefaultWidth。
⇒ 不单独测边界,就看不出"它走的是第二个分支" ✓ -
★★★★ dump 里出现"预期之外的字符",要回源码找来源。
标签文本带×⇒ 回源码发现是 JSX 实体×硬编码的(不可配置)
⇒ 而且源码里还有分支:labelExtractor返回 React 元素时不加×——
这是"想去掉×"的正规做法 ✓ -
★★★
placeholder/accessibilityLabel这类"属性"不进布局 dump。
本轮placeholder="Start typing"未命中 ⇒ 如实记"未观察到",不写成"没生效" ✓
本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-tag-input(上游 0.0.21 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-tag-input |
| 适配 TAG | 0.0.21-ohos-1.0.0 |
| 需要 HAR / 权限 / ohpm | 都不需要(纯 JS) |
| 库类型 | 标签输入框(标签 + × 删除 + 末尾 TextInput) |
| 运行时依赖 | invariant(需装)+ prop-types(宿主已有) |
| 导出形态 | 只有默认导出 TagInput(class,extends React.PureComponent) |
| 需要 polyfill | ViewPropTypes / Text.propTypes(工程里已有) |
| static 纯函数 | TagInput.inputWidth(text, spaceLeft, inputDefaultWidth, wrapperWidth) |
| 许可证 | MIT |
| 上游仓库 | https://github.com/jwohlfert23/react-native-tag-input |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = TagInputTestApp) |
npm install "git+https://atomgit.com/oh-react-native/react-native-tag-input.git#0.0.21-ohos-1.0.0" --ignore-scripts
npm install invariant --ignore-scripts --no-audit --no-fund
# 另需 ViewPropTypes / Text.propTypes 的 polyfill(工程里已有)
import TagInput from 'react-native-tag-input';
const [tags, setTags] = useState(['标签一', '标签二']);
const [text, setText] = useState('');
const ref = useRef(null);
<TagInput
ref={ref}
value={tags}
onChange={(next) => setTags(next)}
text={text}
onChangeText={(t) => setText(t)}
labelExtractor={(tag) => String(tag)} // 必填
tagColor="#d8f0d8"
tagTextColor="#004400"
inputDefaultWidth={90}
maxHeight={75}
/>
// 实例方法(箭头类字段 ⇒ 不在 prototype 上,用 ref 调)
ref.current.focus();
ref.current.removeIndex(0);
ref.current.scrollToEnd();
// 静态纯函数
TagInput.inputWidth('', 0, 90, 1320); // 90
TagInput.inputWidth('abc', 150, 90, 1320); // 140
TagInput.inputWidth('abc', 50, 90, 1320); // 1320
TagInput.inputWidth('abc', 100, 90, 1320); // 90(边界走 >= 分支)
// ⚠️ 它是 PureComponent(prototype.isPureReactComponent === true)
// ⚠️ 需要 ViewPropTypes / Text.propTypes 的 polyfill
// ⚠️ 标签末尾的 × 是硬编码的(×),想去掉就让 labelExtractor 返回 React 元素
// ⚠️ focus / removeIndex / scrollToEnd 是箭头类字段(不在 prototype 上)
// ⚠️ placeholder 不会出现在布局 dump 里
# 换页启动测试页 —— force-stop 不能省
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey TagInputTestApp
判据的复现方式
# ① 23 条判据(零 FAIL)
hdc shell "hilog -x | grep tgi-test" | Select-String 'PASS|FAIL|SUMMARY|NOTE'
# ⇒ SUMMARY | 合计 23/23 通过,失败 0
# ⇒ PureComponent / propTypes 16 项 / defaultProps 6 项精确值
# ⇒ 方法归属两个方向都过 / inputWidth 4 条
# ② 渲染证据
devecocli ui layout | Select-String 'TGI 标签|TextInput'
# ⇒ 「TGI 标签一 ×」x=78 y=1244、「TGI 标签二 ×」x=371
验证环境
| 项 | 版本 |
|---|---|
| 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 |
| 宿主依赖 | invariant(新装)+ prop-types(已有)+ 已有 ViewPropTypes/Text.propTypes polyfill |
| 宿主 HAP 产物 | entry-default-signed.hap |
| 本次构建 | 一次成功:assembleHap 3 分 37 秒(打包前两条 JSX 自查,风险行 = 0) |
| 验证规模 | 23/23 全 PASS、零 FAIL;PureComponent 判定;propTypes 16 项 + defaultProps 6 项精确值;方法归属两档双向验证;inputWidth 4 条(含边界);文本 3 处 + TextInput 2 个;LogBox 无通知;4 张截图互异 |
⚠️ 基线说明:交付包
spec.json声明RN 0.88.0 + RNOH 0.84.4(两字段自相矛盾),本次验证在RN 0.84.1 + RNOH 0.84.3上完成。
本库零原生代码,平台差异只在两处Platform.OS(都不影响鸿蒙)⇒ 结论跨基线成立 ✓
相关社区与项目地址
| 项目 | 地址 |
|---|---|
| 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)