React Native 0.85 更新日志精读
副标题:一起看看 React Native 0.85 里值得关注的变化:从 Shadow Tree 提交分支(commit branching)到新的 Animation Backend。
- 原文:React Native 0.85 changelog dive
- 作者:Jacek Pudysz
- 原文发布:2026 年 4 月 9 日 · 约 9 分钟阅读
引言
每一次 React Native 发布都会附带很长的 changelog,但我们大多数人只会扫一眼 breaking changes 就继续干活。这其实错过了很多东西。changelog 是一扇窗,能看见框架正在往哪走;而 0.85 是作者心目中近年来最令人兴奋的版本之一。
作为维护两个会直接向 Shadow Tree 提交(Uniwind Pro 与 Unistyles)的库的开发者,作者读 0.85 changelog 的每一行时,心里只有一个很具体的问题:什么会坏掉?又会打开哪些新可能?
本文会带你过一遍最吸引作者注意的改动,并说明它们如何影响(或将会如何影响)这些库。
下面开始。
Shadow Tree 提交分支(commit branching)
若只能选一个最能定义本次发布的改动,作者会选由 @j-piasecki 引入的 Fabric commit branching 机制。它横跨四个相互关联的 PR(#54833、#54835、#54836、#54837),并从根本上改变 React Native 如何把更新提交到 Shadow Tree。
那么,问题是什么?
在 0.85 之前,每一次提交(无论来自 React,还是来自动画等原生来源)都会更新同一个 _currentRevision__(源码字段名)。这意味着:当 Uniwind Pro 的 C++ 引擎把样式更新推送到 Shadow Tree 的同时,React 又因状态变化提交了一次,它们会在同一条 revision 上「抢」。这会在 JS 线程与主线程之间制造争用,在密集更新周期里可能导致竞态与资源耗尽。
新方案引入双分支(dual branching):
- React 始终提交到专用的
_currentReactRevision__(JS 分支) - 其他来源(动画、原生状态更新等)继续使用
_currentRevision__(主分支) - 在 event loop pass 结束时,一次 「merge」 会把 JS revision 提升为新的主 revision
PR 描述里最关键的一点是:「merge」意味着把 JS revision 提升为新的 main,并丢掉在 fork 与 merge 之间发生在主分支上的改动。保留这些改动是制造改动的一方的责任,它们应通过 commit hook 重新应用。
merge 被安排在主线程执行,从而避免 Shadow Tree 被「榨干」——因为主分支上的提交现在都发生在同一条线程上。
该能力由
enableFabricCommitBranching特性开关控制:默认尚未开启,但基础设施已就位,可供测试。
这对 Unistyles 与 Uniwind Pro 意味着什么?
两个库都会从 C++ 直接把样式更新提交到 Shadow Tree。在这种分支模型下,我们的提交落在主分支(我们是「其他来源」,不是 React)。React 的提交走 JS 分支。到这一步为止都很好:两边不再争用同一条 revision。
但关键在于:当 JS revision merge 时,会整体替换主 revision。我们在 fork 与 merge 之间提交到主分支上的任何改动都会丢失。PR 写得很明确:库必须使用 commit hook,在 merge 之后重新应用自己的改动。
Uniwind Pro 已经在用 commit hook(这也是作者能在 React 二次渲染之间持久化样式覆盖的原因),但需要确保:hook 能在新的 JS revision merge 之后正确重放样式,而不仅仅是在「普通 React 提交」之后。等该 flag 默认开启后,作者会大量测试。若你的库也会从原生代码向 Shadow Tree 提交,请把这点记在心上。
Animation Backend 现为默认
第二大变化是:C++ Animation Backend 现在默认启用。以前它藏在编译开关 -DRN_USE_ANIMATION_BACKEND 后面;现在它已成为每一次 React Native 构建的核心部分。
Animation Backend 到底是什么?
它是 React Native renderer 的一部分,让动画框架可以在不走 React JavaScript 渲染管线的情况下更新 React 组件的 props。动画相关的提交不再与 React、样式库一起在 Shadow Tree 上「抢位置」,而是被挪到独立、专用的机制里。
后端大致由这些关键组件协同工作:
AnimationBackend:管理用户回调;通过快速的synchronouslyUpdateProps路径(非布局变更)或完整 Fabric commit(发生布局变更时)应用更新AnimatedProps:注册回调返回的结构,表示对特定组件的更新AnimationBackendCommitHook:通过写入AnimatedPropsRegistry,把动画更新与 React 提交同步AnimationChoreographer:包装平台原生帧回调(iOS 上RCTDisplayLink,Android 上Choreographer)
动画框架通过 start 方法注册回调,并拿到 CallbackId。每一帧回调会收到 AnimationTimestamp,并返回 AnimationMutations 列表:每一项把 ShadowNodeFamily(要更新哪个组件)与其新 props 配对。stop 用 CallbackId 取消;trigger 则为手势等事件触发同步更新。
尤其有趣的是它如何与 React 同步:AnimationBackendCommitHook 会在 React 提交期间恢复被覆盖的属性,避免 React 二次渲染时把动画值意外重置。
usage.cpp
// start() takes a callback, returns a CallbackId
auto id = backend->start([](AnimationTimestamp timestamp) -> AnimationMutations {
// Return mutations: which components + what props
return {
.batch = {{
.tag = viewTag,
.family = shadowNodeFamily,
.props = animatedProps,
.hasLayoutUpdates = false,
}},
};
});
// Stop by ID when animation completes
backend->stop(id);
还有一个新机制:Animated 可以提示后端在 JS 线程把变更推送到 Shadow Tree。动画结束时,会执行一次 scheduled commit,迫使 commit hook 通过 RSNRU 把更新后的 ShadowNode 推回给 React,从而保证 React renderer 收到动画值的最终状态。
这是作者最期待的变化之一。当下,多个库(Reanimated、Unistyles、Uniwind Pro)会各自独立向 Shadow Tree 提交,并可能互相覆盖。把动画提交挪到单独机制,是走向「更可预期的 Shadow Tree 更新」的一步:每一方都有一条更清晰的通路来应用变更,而少互相踩脚。
Software Mansion 正与 Reanimated 一起把这条路打磨得更顺。等 Animation Backend 从 React Native 0.85.1 起无需 flag全面落地后,作者会探索 Uniwind Pro 与 Unistyles 如何同样受益。若最终能在「非 React 的 Shadow Tree 更新」上收敛到共享机制,将解决一大类库作者今天要面对的隐蔽 bug。
本 release 中 Animation Backend 相关改动的主要贡献者之一是 Software Mansion 的 Bartlomiej Bloniarz:choreographer、互斥安全、AnimatedPropsRegistry 清理等工作都很出色。若你想进一步了解,可看他为 Animation Backend 补充的 RNTester 示例。
cloneMultiple API 变更
这条直接影响作者团队。cloneMultiple 现在要求传入 std::shared_ptr<const ShadowNodeFamily>,而不再是裸指针。commit解释了原因:视图卸载时,持有的 ShadowNodeFamily 的 shared_ptr 可能被释放,裸指针会变成悬空指针。
这尤其与 Animation Backend 有关:Reanimated 会持有 ShadowNode 引用,所以旧方案对它更「顺手」;但 Animation Backend 并不以同样方式持有引用,动画仍在运行时视图就可能卸载。
Unistyles 与 Uniwind Pro 都用 cloneMultiple 在多个组件间批量更新样式。我们需要把 C++ 代码更新为传递拥有所有权的 shared_ptr 实例。修复本身直接,但若你也是使用该 API 的库作者,别漏掉这一点。
fbjni 中的 StateWrapper
对原生模块作者来说,这是一条小但重要的改动:@mrousavy 把 StateWrapper 加进了 C++ fbjni 类型体系。
以前若你用 Fabric 构建原生视图,并需要在 C++ 里处理 state(例如通过 Nitro Modules),你得自己定义 StateWrapper 实现并做不安全的向下转型。现在官方 StateWrapper 基类通过 fbjni 暴露出来,支持类型安全的继承,以及正确的 jni::dynamic_ref_cast,而不是不安全的 static_cast。
这对生态是好事:我们维护的不安全转型越少,用户遇到的「神秘崩溃」就越少。
View Transition 特性开关
0.85 里落地了一个新的特性开关:viewTransitionEnabled。它控制 View Transition API:用于视图之间的动画过渡,例如更顺滑的导航过渡与 UI 元素更新。
默认值为 false,因此目前只是面向未来 release 的基础设施,但值得持续关注。Uniwind Pro 已经提供动画化主题切换(圆形 reveal、模糊、淡入淡出等)。若 React Native 引入自己的 View Transition API,未来也许能借平台能力让主题过渡更顺滑、更一体化。
文本渲染变化
一个细微但重要的视觉变化:默认情况下,borderRadius 边界外的文本会被裁切隐藏。
在 0.85 之前,Text 默认相当于 overflow: visible:文本可能画在边框之上,并超出 borderRadius 边界。原生实现也不一致,混合了 hidden / visible 的 overflow 行为。
现在文本默认按 padding box 裁切,从而让 Fabric 样式层与原生平台行为更一致。若你的应用曾依赖文本溢出圆角(无论有意无意),可能需要在相关组件上显式设置 overflow: 'visible'。
对 Uniwind 与 Unistyles 用户来说,通常不需要库侧改动:这是 React Native 的渲染行为变更,样式引擎会把你设置的 overflow 原样透传。
其他破坏性变更
StyleSheet.absoluteFillObject 已移除。 它在 0.82 被标记废弃,现在正式删除。请改用 StyleSheet.absoluteFill:
Migration.tsx
// Before
const style = { ...StyleSheet.absoluteFillObject };
// After
const style = { ...StyleSheet.absoluteFill };
Yoga 迁移到 Kotlin
另一个有趣变化:YogaNode、YogaConfigJNIBase 与 YogaProps 已由 @mateoguzmana 迁移到 Kotlin。这是 React Native Android 代码现代化的一部分:行为不应改变,但会让更偏好 Kotlin 的 Android 开发者更容易接触 Yoga 集成层。
总结
作者对这次发布非常兴奋。也许还需要几个月,特性才会在默认路径上完全落地,但方向清晰,对 Unistyles 与 Uniwind Pro 的影响也会很大。
Shadow Tree commit branching 让原生侧更新模型更干净;Animation Backend 把动画提交挪到独立机制,为跨库的可预期 Shadow Tree 提交打开大门。合在一起,它们可能解锁全新模式:样式驱动的动画、共享元素过渡,以及那些今天还需要「和 renderer 搏斗」才能实现的能力。
作者会在未来文章里更深入覆盖 Animation Backend。敬请期待!
Happy coding!
原文页脚(保留站点信息)
- 站点:React Native Crossroads
- Jacek Pudysz · Copyright © 2026