0day 适配 RN 0.88.0-rc.1:一次 34 小时的接力

0 评论 1096 浏览 0 收藏 15 分钟

上游 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 小时,接棒的人就交出了第一个提交——而这个人换了。 这意味着两件事:

  1. 适配路径真的标准化了。 上次沉淀下来的流程(怎么切基线、怎么改补丁、怎么验证、怎么写文档),这次被另一个人完整走通了一遍。这说明它不是”某个人的手艺”,是可以被别人复用的方法
  2. 接力棒有人接。 这是开源项目能活下去的唯一标志——不看你某一次冲得多快,看的是下一次还有没有人来

所以这篇文章的标题才叫“接力”。

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 协议。

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
海报
评论
评论请登录
  1. 目前还没评论,等你发挥!