React Native for OpenHarmony 实战:三方库 react-native-skeleton-placeholder 的鸿蒙化适配指南
本文记录把 react-native-skeleton-placeholder(骨架屏占位图)适配到 HarmonyOS 的完整过程。
它做的事很专一:把真实内容"骨架化" —— 递归遍历 children,把每个带文字/叶子的节点替换成灰块,再用一层渐变高光在这些灰块上扫过,形成"加载中"的观感。
结论:组件本体可以跑,三种渲染分支都验证到了;但 shimmer 的高光层(LinearGradient)一次都没有渲染 —— 这是一个已钉死的现象。
另外它有两个原生 peer 依赖,本文会把"它们的鸿蒙适配版是否存在"和"我为什么选择打桩"都写清楚。
★★★★★ 一、两个原生 peer 依赖:都存在,但我选择了打桩
库的 package.json:
"peerDependencies": {
"react": ">=0.14.8",
"react-native": ">=0.50.1",
"react-native-linear-gradient": "^2.5.6",
"@react-native-masked-view/masked-view": "^0.2.8"
}
而源码里它们是无条件 require 的(lib/skeleton-placeholder.js L36 / L39):
const masked_view_1 = __importDefault(require("@react-native-masked-view/masked-view"));
const react_native_linear_gradient_1 = __importDefault(require("react-native-linear-gradient"));
⇒ 两个都必须能被解析(否则打包失败),而它们都是原生库。
它们的鸿蒙适配版都存在 ✓:
| 上游 | 鸿蒙适配版 |
|---|---|
react-native-linear-gradient@2.8.3 | @react-native-ohos/react-native-linear-gradient@3.2.0 ✓(⚠️ 版本与 peer 的 ^2.5.6 不一致) |
@react-native-masked-view/masked-view@0.3.2 | @react-native-ohos/masked-view@0.4.3 ✓(⚠️ 包名不同,需 metro 映射) |
但我这一轮选择打桩隔离,理由是风险已知:HAR 接入此前实测失败过一次 ——
hvigor ERROR: Error Code: 00308018 Unknown Error
hvigor BUILD FAILED in 2 s 540 ms ← 只 2.5 秒 ⇒ 配置/解析期错误
⇒ 不在这一轮同时赌两个 HAR ✓ ⇒ 代价:shimmer 的渐变效果不可见(这一代价后来变成了本轮最有价值的发现,见第四节)。
★★★★ 桩的设计:带调用计数
// stubs/react-native-masked-view.js
let renderCount = 0;
const MaskedView = (props) => {
renderCount += 1;
console.log(`[masked-view-stub] NOTE | MaskedView 被渲染(累计 ${renderCount} 次)…`);
const {children, maskElement, style, ...rest} = props;
return <View {...rest} style={style}>
<View testID="masked-view-stub-mask">{maskElement}</View> {/* 骨架块 */}
<View testID="masked-view-stub-content">{children}</View> {/* 底色 + 渐变 */}
</View>;
};
linear-gradient 的桩同理(并会打印它收到的 colors / start / end)。
⇒ 这样一个"空桩"就变成了判据:它被渲染几次,就能反推组件的分支走到了哪里 ✓(见第三节)
★★★★★ 二、源码的三段渲染逻辑(这是理解一切的关键)
const [layout, setLayout] = React.useState();
const isAnimationReady = Boolean(speed && layout?.width && layout?.height);
if (!enabled || !placeholders) return children; // ① 关闭 ⇒ 直接返回 children
if (!layout?.width || !layout?.height)
return <View onLayout={(e) => setLayout(e.nativeEvent.layout)}>{placeholders}</View>; // ② 未就绪 ⇒ 纯 View
return (<MaskedView style={{height: layout.height, width: layout.width}} maskElement={placeholders}>
<View style={[StyleSheet.absoluteFill, {backgroundColor}]} />
{isAnimationReady && highlightColor !== undefined && transparentColor !== undefined && (
<Animated.View style={animatedGradientStyle}>
<LinearGradient colors={[transparentColor, highlightColor, transparentColor]} … />
</Animated.View>
)}
</MaskedView>); // ③ 就绪 ⇒ MaskedView + 渐变
⇒ 三段分支非常清晰:
- ①
enabled=false⇒ 原样渲染 children - ②
onLayout未就绪 ⇒ 先用纯View占位,同时靠onLayout量自己的宽高 - ③ 就绪 ⇒
MaskedView(遮罩=骨架块)+ 底色层 + shimmer 渐变层
★★★★★ 三、三段分支 + layout 链路全部验证到(桩计数是功臣)
实测(三个实例:S1 两个 Item、S2 嵌套 Item、S3 enabled={false}):
NOTE | ★ MaskedView 桩渲染次数 = 2
NOTE | ★ LinearGradient 桩渲染次数 = 0 ← ★ 见第四节
桩的渲染日志:
[masked-view-stub] NOTE | MaskedView 被渲染(累计 1 次)—— 说明 layout 已就绪、走到了 MaskedView 分支
[masked-view-stub] NOTE | MaskedView 被渲染(累计 2 次)…
⇒ 恰好 2 次 = S1 + S2(S3 是 enabled=false ⇒ 走分支① ⇒ 不渲染 MaskedView ✓)
⇒ ⇒ 一段桩计数,同时验证了"分支① 被短路"和"分支②→③ 的迁移发生了 2 次" ✓✓
再看 layout 是否真的正确 —— 我的桩把 style={{height: layout.height, width: layout.width}} 原样透传,所以从 dump 就能读到 layout:
Custom#masked-view-stub [54,570,1266,798] ⇒ 1212 × 228
Custom#masked-view-stub [54,955,1266,1315] ⇒ 1212 × 360
对照 S1 的构成:两个 Item 是 height=40 + height=24 + marginTop=12
⇒ 40 + 24 + 12 = 76vp ⇒ 76 × 3 = 228px ⇒ 与第一个 MaskedView 的 h=228 精确一致 ✓✓
对照 S2:外层 Item height=120 ⇒ 120 × 3 = 360px ⇒ 与第二个 h=360 精确一致 ✓✓
⇒ ⇒ "onLayout 量到了正确的宽高 + transformToPlaceholder 的尺寸传递正确"这两个环节,用两个独立数字闭环验证了 ✓✓
⇒ (顺带说明:onLayout 在 RNOH 上是可靠的,这也再次印证了"该用 onLayout、不该用 measure()")
★★★★★ 四、核心发现:MaskedView 渲染了,但 shimmer 的 LinearGradient 一次都没渲染
NOTE | ★ MaskedView 桩渲染次数 = 2
NOTE | ★ LinearGradient 桩渲染次数 = 0
FAIL | ★★★ LinearGradient 桩被渲染 ⇒ shimmer 分支条件全部成立(>0) | 期望=true | 实际=false
把条件逐项推理一遍(渲染分支③的条件是 isAnimationReady && highlightColor !== undefined && transparentColor !== undefined):
| 条件 | 推理 | 结论 |
|---|---|---|
speed | 默认 800(未传) | ✅ 真 |
layout.width / layout.height | 上面已量到 1212 × 228,两者都 > 0 | ✅ 真 |
⇒ isAnimationReady | Boolean(800 && 1212 && 228) | ✅ 应为真 |
highlightColor | 默认 '#F2F8FC' | ✅ 非 undefined |
transparentColor | getTransparentColor('#F2F8FC') ⇒ '#F2F8FC00' | ✅ 非 undefined |
⇒ ⇒ 三个条件按推理都应成立,但 LinearGradient 就是没渲染 ✗
能确定的位置:问题不在 layout 链路(已闭环验证),也不在颜色解析(否则整个组件会抛错、而组件渲染正常),
所以收敛到"<Animated.View style={animatedGradientStyle}> 这一层":
// animatedGradientStyle = {...absoluteFillObject, flexDirection:'row',
// transform:[{translateX: animatedValueRef.current.interpolate({inputRange:[0,1], outputRange:[-w, w]})}]}
// 动画:Animated.loop(Animated.timing(v, {toValue:1, duration:speed, easing:Easing.ease, useNativeDriver: true}))
⇒ useNativeDriver: true 是这一层最可疑的地方 —— 它驱动的是 translateX(transform,本来是可以原生驱动的)✓
⇒ ⇒ 但在这套 RNOH 上,"原生驱动 + Animated.loop + interpolate"这个组合是否让子树的渲染被跳过,本轮没有直接证据 ⚠️
⇒ 如实记为:现象已钉死(LinearGradient 0 次)、根因收敛到 Animated.View 那一层、未最终确认。
⚠️ 这一节也说明了一个诚实的局限:打桩能验证"结构与分支",但验证不了"被桩替换掉的那层效果"。
而恰恰是"渐变层"出了问题 —— 如果我一开始就接了 HAR,可能会更早看到它(也可能更早被 HAR 的坑卡住)。两条路各有取舍 ✓
★★★ 五、骨架块几何:Item 的 width/height 直接变成块尺寸
实测(dump 里的真实块):
| 实例 | Item 的 props | 预言(density 3) | 实测 |
|---|---|---|---|
| S1 第 1 个 | width=200 height=40 | 600 × 120px | ✅ w=600 h=120 |
| S2 外层 | width=260 height=120 | — / 360px | ✅ h=360(宽度是满宽:容器分支没有固定宽度) |
S3(enabled=false) | 真实内容 200×40 | 600 × 120px | ✅ w=600 + 文本「真实内容 200x40」 |
⇒ Item 的 height 精确生效 ✓
⇒ width 只在"叶子块"上生效(S1 第 1 个 = 600px ✓);有子节点的"容器块"宽度会被父容器拉满(S2 外层 = 1212px)——
这与源码一致:容器走 styles.placeholderContainer(只有 backgroundColor:'transparent',没有固定宽度)✓
enabled={false} 的 A/B 也很干净:找到文本 「真实内容 200x40」 ✓ ⇒ 说明它走的是分支①(直接返回 children),没有被骨架化 ✓
★★ 六、契约与其余预检
契约(全 PASS):
NOTE | 默认导出的 typeof = function
PASS | ★ 默认导出是函数组件(function)
PASS | ★★ 带静态属性 Item(库的复合组件写法)
NOTE | Item.displayName = SkeletonPlaceholderItem
PASS | ★★ Item.displayName = 'SkeletonPlaceholderItem'
PASS | ★ 是函数组件(无 prototype.render)
⇒ SkeletonPlaceholder.Item 是"函数组件上挂静态子组件"的写法 ⇒ 所以判定它"是不是组件"要同时看函数和静态属性 ✓
其余预检:
| 预检项 | 结果 |
|---|---|
| 老写法九项 | ✅✅ 全部为空 |
Platform.OS | ✅ 空 |
measure() / measureInWindow | ✅ 空(用的是 onLayout) |
onLayout | 1 处 —— RNOH 上可靠,本轮已闭环验证 ✓ |
useNativeDriver | 1 处 true(驱动 translateX/transform ⇒ 用法本身正确) |
NativeModules / requireNativeComponent | ✅ 空(原生能力在 peer 里) |
Dimensions | 模块顶层读 window.width = 440 ✓ |
prepack | ✅ npm install 不跑 |
一条 React 警告(非崩溃):
"{}: `key` is not a prop. Trying to access it will result in `undefined` being returned…"
⇒ 与库的 transformToPlaceholder 里 <View key={index} … children={…} /> 这种写法有关(把 children 当 prop 传)⇒ 只是警告 ✓




七、接入与预检(要点)
# ① 装库
npm install "git+https://atomgit.com/oh-react-native/react-native-skeleton-placeholder.git#5.2.4-ohos-1.0.0" --ignore-scripts
# ② 它的两个原生 peer —— 二选一:
# (A) 接真库(功能完整,但两个 HAR 都要接)
npm install --ignore-scripts react-native-linear-gradient @react-native-masked-view/masked-view
# + 在 oh-package.json5 里声明:
# @react-native-ohos/react-native-linear-gradient → node_modules/.../harmony/*.har
# @react-native-ohos/masked-view → node_modules/.../harmony/*.har
# (B) 打桩隔离(零原生接线,代价:shimmer 不可见)—— 本文采用
# metro.config.js 的 resolver.extraNodeModules:
# '@react-native-masked-view/masked-view': <stub>
# 'react-native-linear-gradient': <stub>
八、交付包质量与基线说明
| 类别 | 结果 |
|---|---|
| 交付包内容 | 只有 lib/(skeleton-placeholder.js 9796 B + .d.ts)⇒ 可读但无 src/ ⚠️ |
spec.json | ⚠️ 缺 upstreamCommit |
validation | ⚠️ "build-pass-device-pending" |
| ⚠️ 声明的基线 | reactNative: "0.88.0" + rnoh: "0.84.4" ⇒ 两字段自相矛盾 |
| ⚠️ 许可证 | GitHub 记 MIT、npm 与交付包记 ISC —— 不一致 |
| 备注 | 实现零改动(本轮未修改库任何文件) |
关于基线:本次验证在 RN 0.84.1 + RNOH 0.84.3 上完成。库无已移除写法 ⇒ 契约结论跨基线成立;
但第四节的 shimmer 问题与 Animated 的原生驱动有关,换基线时值得复验。
九、已知限制与本次验证边界
9.1 用这个库要知道的
| 事项 | 说明 |
|---|---|
★★★★ 两个原生 peer:react-native-linear-gradient + @react-native-masked-view/masked-view | 都要能被解析;鸿蒙适配版分别是 @react-native-ohos/react-native-linear-gradient@3.2.0 与 @react-native-ohos/masked-view@0.4.3 |
| ★★★★ 在 RNOH 上 shimmer 高光层没有渲染 | LinearGradient 桩渲染 0 次 ⇒ 只剩底色层;(本次用桩验证,根因收敛到 Animated.View 层) |
| ★★★ 三段渲染分支 | enabled=false ⇒ 原样 children;onLayout 未就绪 ⇒ 纯 View;就绪 ⇒ MaskedView |
★★★ 骨架块尺寸 = Item 的 props | Item 是叶子 ⇒ width/height 都生效;Item 有子节点 ⇒ 宽度被父容器拉满 |
★★★ onLayout 链路可靠 | 实测 layout.height 与子项之和精确一致(228 = 76vp×3;360 = 120vp×3) |
★★ 会有一条 key is not a prop 的 React 警告 | 库把 children 当 prop 传导致,仅警告 |
★ Item.displayName = 'SkeletonPlaceholderItem' | 可断言的契约 |
9.2 本次验证的边界
- ★★★ shimmer 高光效果【未验证】(
LinearGradient用桩替换,且实测桩 0 次渲染 ⇒ 渐变这一层完全没看过); - ★★★ 没有接真 HAR(两个原生库的鸿蒙适配版存在但未接入)⇒ "接了 HAR 之后 shimmer 是否就正常"未知;
- 未验证
speed/direction/shimmerWidth/highlightColor各取值的实际效果; - 未验证
borderRadius的圆角效果(dump 不反映); - 未验证 更深的嵌套(3 层以上)与 Fragment children;
- 未验证 children 是字符串/数组时的骨架化结果;
- 未验证
Item的style与零散 props 混合时的优先级(只用了零散 props); - 未验证 多实例同时存在时的性能与
Animated.loop行为; - 未在
RN 0.88.0/RNOH 0.84.4上复验; - 未测其它屏幕宽度;未做跨平台对照。
未改动库代码。
十、常见问题
Q1:它在 RN 0.84 上能跑吗?
组件本体能跑:三段渲染分支都验证到(enabled=false 原样渲染 ✓、onLayout 链路 ✓、MaskedView 分支走了 2 次 ✓),
骨架块几何精确(Item 200×40 ⇒ 600×120px)。
但 shimmer 的高光渐变层没有渲染(见第四节)。
Q2:npm install 报 Unable to resolve module @react-native-masked-view/masked-view?
它和 react-native-linear-gradient 是两个原生 peer,必须能被解析。两条路:
接真库(@react-native-ohos/masked-view + @react-native-ohos/react-native-linear-gradient + HAR),
或者在 metro 里给它们指向空桩(本文采用,代价是 shimmer 不可见)。
Q3:为什么骨架块出来了,但没有"扫过去的高光"?
在本次(打桩)环境下,LinearGradient 的渲染次数是 0 —— 也就是根本没渲染高光层。
按源码推断,isAnimationReady(依赖 layout)与颜色条件都应成立,所以问题收敛到
<Animated.View style={…translateX 插值…}> 这一层(它用的是 useNativeDriver: true)。
建议:接上真 HAR 后复验;若仍无高光,可试着把该动画改成 useNativeDriver: false 观察。
Q4:onLayout 在 RNOH 上可靠吗?
可靠。 本轮用它做了闭环验证:MaskedView 的 style 用的就是 layout 的宽高,
实测 228px = (40+24+12)vp × 3、360px = 120vp × 3,两组都精确一致 ✓
(对照:用 measure() 量布局在 RNOH 上拿不到值 —— 这个库选 onLayout 是对的。)
Q5:enabled={false} 有什么用?
用来"临时关掉骨架屏、直接显示真实内容",常用于数据加载完成后切换。
实测它会让组件直接返回 children(不再走骨架化,也不渲染 MaskedView)✓
Q6:怎么让骨架块有圆角?
Item 上的零散 props 会被当作样式:<SkeletonPlaceholder.Item width={200} height={40} borderRadius={8} />
⇒ getItemStyle 会把非 children/style 的 props 当样式对象 ✓
Q7:为什么控制台有 key is not a prop 的警告?
库内部把 children 当 prop 传给 View(<View key={index} … children={…} />)⇒ React 会警告。
仅警告,不影响功能。
小结
react-native-skeleton-placeholder 是函数组件 + 静态 Item 子组件的骨架屏库,
三段渲染分支清晰(enabled=false / onLayout 未就绪 / 就绪后 MaskedView),九项老写法全空。
最值得记下来的是这几条:
-
★★★★★ 两个原生 peer 依赖,但"打桩 + 计数"能验证出很多。
react-native-linear-gradient与@react-native-masked-view/masked-view都是原生库
(鸿蒙适配版@react-native-ohos/react-native-linear-gradient@3.2.0/@react-native-ohos/masked-view@0.4.3都存在);
我给它们打了带调用计数的桩 ⇒ 计数直接反推了"分支走到了哪一步" ✓ -
★★★★★ 一段桩计数验证了两件事:
MaskedView渲染 2 次 = S1 + S2 ✓,
而 S3(enabled=false)一次也没渲染 ✓ ⇒ 分支①被短路 + 分支②→③迁移两次,一次拿到 ✓ -
★★★★★
onLayout链路做了闭环验证。
桩原样透传style={{height: layout.height, width: layout.width}}⇒ dump 读到228/360
⇒ 与(40+24+12)vp×3、120vp×3精确相等 ✓✓
⇒ "onLayout量的值"与"子项尺寸之和"互相印证 ✓ -
★★★★★ 一个已钉死的适配问题:
LinearGradient渲染 0 次。
MaskedView渲染了、layout完全正确、颜色解析不会抛错 ⇒ 条件按推理都该成立,但高光层没渲染 ⇒
根因收敛到<Animated.View>(useNativeDriver: true+translateX插值)那一层 —— 未最终确认,如实标注 ✓ -
★★★ 骨架块尺寸规则:
Item是叶子 ⇒ width/height 都生效(200×40 ⇒ 600×120px✓);
Item有子节点 ⇒ 宽度被父容器拉满(走placeholderContainer,没有固定宽度)✓ -
★★★ 一个诚实的局限:打桩能验证"结构与分支",验证不了"被替换掉的那层效果" ——
而恰恰是渐变层出了问题。若一开始接 HAR 可能更早看到它(也可能更早被 HAR 的坑卡住)。两条路各有取舍 ✓ -
★★ 复合组件的契约写法:
SkeletonPlaceholder.Item是"函数组件 + 静态属性" ⇒
断言要同时看函数与**Item.displayName** ✓ -
⚠️ 我自己的两处问题:
① PowerShell 里写了if ($w==0) {}这种非法语句 ⇒ 刷了一屏报错(但结论没受影响);
② 截图重拍三次才拿到 4/4 —— 因为"滚到底之后画面就不再变化"⇒ 应该先规划好四个不同的位置/状态再拍 ✓
本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-skeleton-placeholder(上游 5.2.4 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-skeleton-placeholder |
| 适配 TAG | 5.2.4-ohos-1.0.0 |
| 需要 HAR / 权限 / ohpm | ⚠️ 两个原生 peer 需要:@react-native-ohos/react-native-linear-gradient / @react-native-ohos/masked-view(本轮打桩未接) |
| 库类型 | 骨架屏占位(骨架化 children + shimmer 高光) |
| 运行时依赖 | ✅ 无 dependencies;⚠️ 两个原生 peer(见上) |
prepack / postinstall | ✅ 没有会阻塞安装的脚本 |
| 许可证 | ISC(GitHub 记 MIT,不一致) |
| 上游仓库 | https://github.com/chramos/react-native-skeleton-placeholder(★733) |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = SkeletonPlaceholderTestApp) |
# 装库
npm install "git+https://atomgit.com/oh-react-native/react-native-skeleton-placeholder.git#5.2.4-ohos-1.0.0" --ignore-scripts
# 两个原生 peer 二选一:
# (A) 接真库:npm install react-native-linear-gradient @react-native-masked-view/masked-view
# + 在 oh-package.json5 里声明 @react-native-ohos/react-native-linear-gradient 与 @react-native-ohos/masked-view 的 HAR
# (B) 打桩:metro 的 resolver.extraNodeModules 指向空桩(本文采用)
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
// ① 基本用法:把真实结构"骨架化"
<SkeletonPlaceholder>
<SkeletonPlaceholder.Item width={200} height={40} borderRadius={8} />
<SkeletonPlaceholder.Item width={300} height={24} borderRadius={4} marginTop={12} />
</SkeletonPlaceholder>
// ② 嵌套:外层是容器(宽度会被拉满),内层是叶子
<SkeletonPlaceholder>
<SkeletonPlaceholder.Item width={260} height={120} borderRadius={12}>
<SkeletonPlaceholder.Item width={120} height={24} borderRadius={6} />
</SkeletonPlaceholder.Item>
</SkeletonPlaceholder>
// ③ 关掉骨架屏:直接显示真实内容
<SkeletonPlaceholder enabled={false}>
<MyRealContent />
</SkeletonPlaceholder>
// ⚠️ 两个原生 peer 必须能被解析(接 HAR 或打桩)
// ⚠️ 在 RNOH 上 shimmer 的高光层实测没有渲染(LinearGradient 桩 0 次)
// ⚠️ Item 有子节点时宽度会被父容器拉满;叶子节点 width/height 都生效
// ⚠️ 会有 'key' is not a prop 的 React 警告(库内部把 children 当 prop 传)
# 换页启动测试页 —— force-stop 不能省
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey SkeletonPlaceholderTestApp
判据的复现方式
# ① 契约 + 桩计数:逐条 PASS/FAIL 日志
hdc shell "hilog -x | grep skp-test" | Select-String 'PASS|FAIL|SUMMARY|NOTE'
# ⇒ Item.displayName、MaskedView 渲染次数、LinearGradient 渲染次数
# ② 桩自己的渲染日志(每次渲染都会打一行)
hdc shell "hilog -x | grep -e masked-view-stub -e linear-gradient-stub"
# ③ 骨架块几何 + layout 闭环
devecocli ui layout | Select-String 'masked-view-stub'
# ⇒ 第一个 stubs 的 box 高 228(= (40+24+12)vp×3)、第二个 360(= 120vp×3)
# ④ enabled=false 的对照:dump 里应出现真实内容文本
devecocli ui layout | Select-String '真实内容'
验证环境
| 项 | 版本 |
|---|---|
| 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 |
| 宿主依赖 | ⚠️ 两个原生 peer 用带计数的空桩隔离 |
| 宿主 HAP 产物 | entry-default-signed.hap |
| 本次构建 | assembleHap 3 分 50 秒 ⇒ 一次成功(打包与构建退出码都检查了) |
| 验证规模 | 契约 8 条全 PASS;桩计数闭环(MaskedView 2 次 / LinearGradient 0 次);layout 双数字闭环(228 / 360 精确);骨架块几何命中(600×120);enabled=false 对照成立;4 张截图互异 |
⚠️ 基线说明:交付包
spec.json声明RN 0.88.0 + RNOH 0.84.4(两字段自相矛盾),本次验证在RN 0.84.1 + RNOH 0.84.3上完成。
契约类结论跨基线成立;shimmer 未渲染这一条与Animated原生驱动有关,换基线时值得复验。
相关社区与项目地址
| 项目 | 地址 |
|---|---|
| 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)