0day 适配 RN 0.88.0-rc.1:一次 34 小时的接力
上游 React Native 刚发出 rc.0,不到 34 小时就跟上了 rc.1,动手的还是一张新面孔。这次只用 5 个提交、一个晚上的 16 分钟,相比上一版的两万多行,像从铺路变成了养护;四个坑全是上游一动手、下游就断的连锁反应。

rc.0 发版后不到 34 小时,rc.1 就跟上了。
这一次,动手的是一张新面孔。
这就是开源最让人踏实的地方:不是某个人一直在跑,而是总有人接得住下一棒。
一、为什么跟得这么紧
先说一个容易被忽略的事实:0.88.0-rc.1 不是我们的版本节奏,是上游的。
上游 React Native 在 RC 阶段会连续发 rc.0、rc.1、rc.2……每一版都在收敛 bug、修破坏性改动。对下游适配方来说,这意味着一个选择:
是每一版都跟,还是攒到一起再跟?
攒到一起看起来更省事——少干几遍活。但代价是:等 GA 出来的时候,你要一次性面对 rc.0 → rc.N 累积下来的所有变化,每一处变化都可能是新的构建报错,而且你不知道是哪一版引入的。
所以我们选了笨办法:上游发一版,我们跟一版。
9 月 16 日晚,0.88.0-rc.0 落地。 9 月 18 日早,0.88.0-rc.1 落地。
中间隔了不到 34 小时。

二、时间线:一个晚上的 16 分钟
把提交拉出来看,这次的节奏和上次很不一样。
上次(rc.0)是周六一整天,从早上 7:51 干到晚上 21:00,一个报错一个报错地啃。 这次(rc.1)是一个晚上的 16 分钟——5 个提交,全部落在 9 月 17 日 19:35 到 19:51 之间。
| 时间 | 事件 |
|---|---|
| 09-16 21:13 | rc.0 发版落地(tag v0.88.0-rc.0) |
| 09-17 19:35 | chore(Upgrade): bump React Native and RNOH to 0.88.0-rc.1
——版本基线切换 |
| 09-17 19:36 | fix(CMake): resolve two 0.88.0-rc.1 build blockers in ReactCommon
——两处构建阻塞 |
| 09-17 19:36 | feat(Config): add setup-env entry for react-native/setup-env resolution
——补模块入口 |
| 09-17 19:51 | fix(TurboModules): report prerelease separately in PlatformConstants
——版本解析修复 |
| 09-17 19:51 | fix(Examples): show prerelease in the tester version badge
——徽标显示修复 |
| 09-18 06:46 | 分支合并进 main |
| 09-18 07:00 ~ 07:09 | 中英文发行说明、CHANGELOG、版本索引落地,v0.88.0-rc.1 发版 |
19:35 到 19:51,16 分钟,5 个提交。
这不是“快”,这是“熟”。 上一次踩过的坑——基线怎么切、补丁怎么写回、类型和 codegen 怎么跟——这次都变成了肌肉记忆。上次花的那个周六,赚到的就是这次的 16 分钟。
规模上也能看出来:这次总共 20 个文件、+470 / -524 行,而 rc.0 那次是 120 个文件、两万多行。
从“铺路”变成了“养护”。
三、技术实录:rc.1 的四个坑
改动量小了,但坑一个都不少——而且这次的坑更有意思:它们全都是“上游一动手,下游就断”的连锁反应。
3.1 CMake 的 OBJECT 目标环
这是这次最硬的一个问题,而且是纯构建期的:CMake 生成阶段直接报错,连编译都进不去。
上游 rc.1 起,react_cxxreact 反向链接了 jserrorhandler。而我们的补丁里,jserrorhandler 又链接 react_cxxreact——两个目标互相链接,形成了环。
CMake 对这种结构零容忍,报出来的是:
strongly connected component
解法是打破环,而不是打断功能:不再做双向链接,改为只把 cxxreact 的头文件搜索路径注入过去——头文件能用,链接关系不成立,环就解了。
3.2 一个”不存在”的头文件
同一批修复里还有一处,性质完全不同:
补丁里新增了一个 RawPropsKey.h。问题是——这个头文件在上游 0.88 的 rc.0 和 rc.1 里都不存在。
它是被 JsErrorHandler 的并行化分支用到的。也就是说,这段代码只在特定开关打开时才需要。之前的写法让它无条件生效,一旦上游没有这个文件,构建就断。
改法很直接:把它和 PARALLELIZATION_ON 绑在一起开关。 用到才引,用不到就不引。
3.3setup-env:一个缺了就会崩的入口
这个是运行链路上的断点。
上游 rc.1 新增了一个 src/setup-env.js 副作用模块。而 RNOH 的 metro 解析器会把 react-native/setup-env 这个请求重定向到本包的根目录——那里本来应该有一个同名的入口文件,但它不存在。
结果就是打包直接失败:
Unable to resolve module
修法是补上包根的 setup-env.js,转引到本包 src/private/setup 下的环境初始化逻辑:
// packages/react-native-harmony/setup-env.js(新增)
“use strict”;
“use client”;
require(“./src/private/setup/setUpDefaultReactNativeEnvironment”).default();
它和 index.js、ts.ts、ets.ets 一样,属于包根入口,随 files 通配一起发布。
这类问题的特征:上游“加了一个文件”,对下游却是“少了一个文件”。因为我们的解析路径是重定向过的,上游的目录结构一变,我们的入口就可能对不上。
3.4PlatformConstants:被截断的版本号
最后一个坑很精巧,藏在版本号解析里。
PlatformConstantsTurboModule 之前用 split(‘.’, 3) 解析版本串。对 0.88.0-rc.1 会发生什么?
‘0.88.0-rc.1’.split(‘.’, 3) → [‘0′, ’88’, ‘0-rc’]
↑ ↑ ↑
major minor patch = 字符串 ‘0-rc’
预发布号 rc.1 被丢掉了,patch 还变成了字符串——这跟上游的类型定义完全不符。
改成按 major.minor.patch[-prerelease] 解析后:
- prerelease 单独下发
- patch 恒为数字
- 缺失或非数字的段回退为 0
这样就和上游 Android 的实现(AndroidInfoHelpers.getReactNativeVersionString)对齐了。
为什么这个 bug 现在才暴露? 因为 rc.0 时代它是 0.88.0-rc.0——split(‘.’, 3) 同样会截断,只是之前没人去读那个字段。等到要区分 rc.0 和 rc.1 了,它才浮出水面。
3.5 顺带修好的:徽标上看不见的 rc
上面那个解析问题,在 tester 应用的页头直接表现为一个可笑的场景:
rc.0 和 rc.1 的徽标长得一模一样。
因为徽标只渲染 major.minor.patch,预发布号根本没参与显示。修法是抽出 formatReactNativeVersion,按上游 ReactNativeVersionCheck 的格式化方式补上 -prerelease——现在真机上明确显示 RN 0.88.0-rc.1。
这个修复很小,但它说明了一件事:预发布阶段,“你现在跑的到底是哪一版”必须一眼可见,否则连自己都没法确认测的是哪个包。
四、这一次,接棒的是新面孔
写到这里,必须说一件比技术更重要的事。
rc.1 的 5 个提交,全部来自一位新的贡献者——uksir。
不是我,不是上次那个从早干到晚的人。是另一个人。
| 提交 | 作者 |
|---|---|
| chore(Upgrade): 版本基线切到 rc.1 | uksir |
| fix(CMake): 两处 ReactCommon 构建阻塞 | uksir |
| feat(Config): 补 setup-env 入口 | uksir |
| fix(TurboModules): 预发布版本解析 | uksir |
| fix(Examples): 徽标显示预发布号 | uksir |
这五条,每一条都带着完整的影响范围说明和验证记录——**“tester 的 Debug 与 Release 两种构建模式均已重新构建验证通过”、“已用 tester 的 bundle-harmony 构建验证可正常解析并产出 bundle”、“已重新生成 bundle 并构建 hap 在真机验证通过”**。
这是把活干到底的写法,不是把活干一半就交出去的写法。
为什么这件事值得单独写一节
上一次 rc.0 发版,我在结语里写过一句话:**“这件事没有 KPI,也没有排期表上的一格。”**
那时候我其实有点隐忧:如果这条线一直靠一两个人扛,它能走多远?
rc.1 给了答案。
rc.0 发版后 22 小时,接棒的人就交出了第一个提交——而这个人换了。 这意味着两件事:
- 适配路径真的标准化了。 上次沉淀下来的流程(怎么切基线、怎么改补丁、怎么验证、怎么写文档),这次被另一个人完整走通了一遍。这说明它不是”某个人的手艺”,是可以被别人复用的方法。
- 接力棒有人接。 这是开源项目能活下去的唯一标志——不看你某一次冲得多快,看的是下一次还有没有人来。
所以这篇文章的标题才叫“接力”。
0day 不是一个人的百米冲刺,是一群人的接力赛。
五、结果:v0.88.0-rc.1 落地
2026 年 9 月 18 日早,0.88 线第二个版本落地。
| 项目 | 结果 |
|---|---|
| 版本 | RNOH v0.88.0-rc.1 |
| 对齐上游 | React Native 0.88.0-rc.1 / React 19.2.3 |
| SDK 基线 | compatibleSdkVersion
/ targetSdkVersion = 26.0.0 |
| 兼容范围 | OpenHarmony SDK API 17 ~ API 26 |
| 配套 | DevEco Studio 26.0.0 Release / HarmonyOS 26.0.0 SDK / Command Line Tools 26.0.0 / Node.js >= 20 |
| 改动规模 | 20 个文件、+470 / -524 行 |
| 发布物 | tag v0.88.0-rc.1 + 发行说明(中英文)+ CHANGELOG |
距离 rc.0 发版,33.9 小时。距离上游 rc.1 发布,不到两天。
顺手把历史的坑也扫了
发版之后还有一件小事:把历史发行说明里 HarmonyOS 26.0.0 Beta1 SDK 的写法统一成了 HarmonyOS 26.0.0 SDK。
这种改动没有任何技术含量,但它属于同一类事情——文档里的不一致,就是在给下一个读文档的人埋坑。 与其让他猜“到底哪个写法是对的”,不如顺手抹平。
六、结语:这次赚到的是”可复制”
上一次(rc.0)的结语,我写了三件事:0day 是选择不是手快、适配的大头是查“为什么变了”、文档和代码同等重要。
这一次,我想加一条,而且是更重要的那条:
第四,真正的成功不是“这次做成了”,是“下次别人也能做成”。
rc.0 那次,我们证明的是这条路走得通。 rc.1 这次,证明的是这条路别人也能走。
这两件事的分量完全不同。前者是能力,后者是传承。一个开源项目如果只有前者,它的命运就绑在具体的人身上——人一走,线就断。有了后者,它才真正变成了社区的东西。
所以这一篇的落点,不是“我们 34 小时就跟上了 rc.1”。
而是:34 小时之后,跟上 rc.1 的,是另一个人。
这才是“点滴成石”真正的含义——不是一个人把石头一块块垒上去,是每个人都往同一个方向垒。
RNOH 是开源的。如果你也在用 RN 做鸿蒙应用,或者只是想为这个生态做点什么——下一棒,随时可以接。
不需要先成为核心贡献者。rc.1 的五个提交里,有版本切换、有构建修复、有模块补齐、有显示修正——没有一个是“只有大神才能做”的事。
- 项目地址:https://atomgit.com/oh-react-native/ohos_react_native
- 提 Issue / 反馈问题:https://atomgit.com/oh-react-native/ohos_react_native/issues
- 看这个版本改了什么:CHANGELOG[1] 与 0.88.0-rc.1 发行说明[2]
上游还在往前跑。下一版,也许就是你接。
参考资料:
[1]CHANGELOG: https://atomgit.com/oh-react-native/ohos_react_native/blob/main/CHANGELOG.md
[2]0.88.0-rc.1 发行说明: https://atomgit.com/oh-react-native/ohos_react_native/blob/main/docs/zh-cn/05-%E8%BF%90%E7%BB%B4/release-notes/react-native-harmony-v0.88.0-rc.1.md
本文由人人都是产品经理作者【nutpi】,微信公众号:【nutpi】,原创/授权 发布于人人都是产品经理,未经许可,禁止转载
题图来自Unsplash,基于 CC0 协议。
- 目前还没评论,等你发挥!

起点课堂会员权益



