React Native 0.87 解读与升级要点
文章首发于: https://feinterview.poetries.top/blog/react-native-0-87-release-overview
本文围绕四个问题展开:
- 严格
TypeScript API成为默认后,哪些报错需要修改业务代码? Metro的性能提升落在哪一段开发流程,应该怎样验证?SwiftPM给iOS工程带来了什么,又有哪些试用边界?- 接入
AGP 9时,为什么还要保留兼容配置?
升级 React Native 时,真正占时间的往往是依赖和工程配置:一个旧的内部路径导入、一段原生构建脚本,都可能卡住整个过程。2026 年 8 月 11 日发布的 React Native 0.87,恰好集中调整了这些位置。这篇文章基于官方发布公告做版本解读,并给出便于评估改动范围的小例子;侧重解释变化及其影响,不承诺一套命令就能迁移所有项目。

# 一、先按影响范围看这次更新
把公告里的变化映射到日常工作,可以得到这样一张表:
| 关注方向 | 先检查什么 | 谁更需要关注 |
|---|---|---|
| 类型与公开接口 | 业务导入路径、组件引用、依赖暴露的类型 | 应用开发者、组件库维护者 |
| 开发工具 | 配置文件、调试加载时间、开发机内存 | 经常调试大体量应用的团队 |
iOS 依赖管理 |
原生库的接入方式、工程定制、干净环境构建 | 原生集成负责人 |
Android 工程 |
插件版本、构建脚本、持续集成环境 | 构建与发布负责人 |
这张表也是我建议的评估顺序:先找到项目实际依赖了什么,再决定要测试哪些变化。一个主要使用公开组件的小应用,和维护多个自定义原生模块的工程,即使从同一版本升级,工作量也会相差很大。
尤其不要把“依赖安装成功”当作升级终点。类型检查、打包、原生编译、实际交互,各自只能验证一部分问题。把它们拆开记录,失败时才能知道应该找业务代码、第三方库,还是构建环境。
# 二、严格类型 API 把公开接口边界落到了工具里
# 为什么需要从源码生成类型
React Native 的实现使用 Flow,而大量应用使用 TypeScript。过去单独维护的类型声明,相当于在实现之外再维护一份接口说明:实现和说明更新不同步,就会出现“编辑器说能用,实际行为却对不上”的情况。从源码生成类型的方向,在 0.80 的稳定 JavaScript API 设计说明中已经提出。
我的理解是,这项工作的价值在维护阶段更明显。开发者可以把类型错误当作检查接口使用方式的入口,库维护者也能更清楚地知道哪些东西属于公开契约。一个内部文件恰好能被导入,并不意味着它适合成为长期依赖。
0.87 默认启用严格类型接口。常见调整包括把深层路径导入改为根入口导入,以及为组件引用使用专门的实例类型。下面以 TextInput 为例,只展示导入路径的变化:
- import TextInput from 'react-native/Libraries/Components/TextInput/TextInput';
+ import {TextInput} from 'react-native';
这个替换适用于根入口已有对应导出的情况。遇到只有内部实现、没有公开替代的能力,需要重新检查用途,不能把每个路径都机械地改成同名根导出。
# 组件引用该怎么写
下面是一个完整的组件示例:输入框引用使用 TextInputInstance,按钮点击时调用实例的 focus() 方法。
import {useRef} from 'react';
import {Button, TextInput, View} from 'react-native';
import type {TextInputInstance} from 'react-native';
export default function SearchField() {
const inputRef = useRef<TextInputInstance>(null);
return (
<View>
<TextInput ref={inputRef} placeholder="输入关键词" />
<Button
title="开始输入"
onPress={() => inputRef.current?.focus()}
/>
</View>
);
}
这里明确区分了用于渲染的组件和用于调用方法的实例。已有的 React.ComponentRef<typeof TextInput> 也仍然有效,无须为了统一写法全部替换。具体规则见严格类型 API 的引用迁移说明。

迁移时,我建议先按错误所在文件分组:自己维护的业务组件直接修;依赖相关错误先找兼容版本;历史测试辅助代码单独检查。这样比先添加一批类型断言更容易留下可维护的结果。
还要注意第三方包交付的内容。官方文档建议保留默认的 skipLibCheck,但它针对声明文件;如果依赖让应用直接导入原始 TypeScript 源文件,例如某个测试入口,这部分仍可能参与应用检查。迁移前准备说明解释了这个边界。
# 临时退出只解决类型检查这一层
确实被依赖阻塞时,可以在现有 tsconfig.json 中合并下面的配置;如果已有其他自定义条件,要一并保留:
{
"extends": "@react-native/typescript-config",
"compilerOptions": {
"customConditions": [
"react-native",
"react-native-legacy-deep-imports"
]
}
}
这个开关会切回旧类型。严格类型模式本身不替换运行时的 JavaScript;0.87 对 src/private/* 导出的移除是另一项会影响运行时的变化,不能靠此配置恢复。参见官方常见问题。
公告给出的旧类型退出窗口覆盖 0.88,并计划在随后版本移除旧类型。若团队采用这个过渡方案,我建议在升级任务中记录具体阻塞依赖和退出条件,避免兼容开关被遗忘。退出窗口说明
# 三、Metro 的收益要在开发流程里衡量
本次 Metro 从 0.84 更新到 0.87。官方报告的收益包括:source map 生成速度约为此前两倍,因映射存储优化而使 Metro 内存占用约减半;TypeScript 和 ESM 配置支持也进入稳定状态。Metro 更新说明
这些数字描述的是工具链。它们没有给出应用滚动帧率、启动耗时或手机端内存的相同比例收益。写升级报告时,最好保留指标的对象:开发机上的打包进程,和用户设备上的应用进程,要分别观察。
如果想判断团队是否从中受益,我会做三组对照:
- 固定业务代码、机器和启动方式,记录冷缓存与热缓存下的打包时间。
- 打开相同页面和调试工具,记录源码及映射加载完成的时间。
- 固定一次调试操作路径,记录
Metro进程的峰值内存,多跑几轮观察波动。
这个方案是本文的测量建议,本文没有提供本地性能实测结果。对照时还应记录锁文件和配置差异,否则同时升级了插件或更换了缓存策略,就很难解释收益来自哪里。
配置迁移同样值得单独安排。通用的 Metro 配置文档列出了 metro.config.mjs、metro.config.mts 等形式,并说明了原生加载 TypeScript 配置对 Node.js 的要求。选用新格式时,要把配置加载能力纳入开发机和持续集成检查。
另外,通用配置页仍有将 YAML 标为弃用的文字,而 0.87 公告明确将 YAML 和 .es6 配置列为移除项。针对这次升级,应按版本公告检查旧配置,并在实际锁定的工具版本下验证加载结果。
如果关注的是应用卡顿,可以接着看站内的React Native 真机性能定位文章,把调试效率和真机体验放在各自的测量环节里。
# 四、SwiftPM 值得试验,现有生产工程仍需审慎评估
Swift Package Manager 是 0.87 新增的实验性 iOS 接入路径,默认方案仍是 CocoaPods。官方明确要求暂不用于生产。SwiftPM 发布说明
对团队来说,它值得观察的地方是原生依赖管理能否更贴近 Xcode 工作流。不过,工程是否更省心,最终取决于应用里那些原生依赖能否顺利接入,尤其是带自定义脚本和二进制资源的库。
在已经保存改动的独立试验分支中,可以按官方入口尝试切换。注意 --deintegrate 会移除工程中的 CocoaPods 集成:
cd ios
npx react-native spm --deintegrate
这条命令会修改工程。试验后应检查版本差异、构建应用并验证原生能力;不要把命令退出成功当作所有依赖已经兼容。
需要撤销集成时,官方提供了反向命令。在同一个 ios 目录执行:
npx react-native spm deinit
首次克隆和持续集成构建前仍需执行一次 npx react-native spm。社区库需要提供 Package.swift;缺失时可以尝试官方的 scaffold 工具,但生成清单之后仍要验证库的实际构建行为。SwiftPM 初始化与限制
相关 RFC #994也讨论了已有原生应用集成、构建脚本和打包形式。它适合帮助理解设计背景;具体可用范围应以版本公告和实际工具输出为准。
我的建议是选一条有代表性的验证路径,例如“启动应用、打开相机、获取权限、返回业务页”,再加一次全新环境构建。空白工程通过只能证明基础接入可行,加入关键原生依赖后,结果才更接近自己的项目。
# 五、AGP 9 支持需要连同工具链一起看
先把 React Native 0.87 公告中的几个版本要求记清楚:
| 项目 | 公告给出的要求或版本 |
|---|---|
Node.js |
至少 22.13.0 |
Kotlin |
最低 2.0+,随版本提供的是 2.2.0 |
Android minCompileSdk |
34 |
Android compileSdk / buildTools |
提升到 37 |
这些数据来自最低工具链要求。其中编译相关字段不能用来直接推断最低可安装系统版本;评估设备覆盖范围时还要检查应用自身的 minSdk 配置。
AGP 9 默认带来了内置 Kotlin 支持和新的构建配置接口,会影响已有插件如何接入构建过程。Android 官方要求在升级时选择迁移到内置支持或退出该行为,见 AGP 9 发布说明。
React Native 0.87 当前建议在 android/gradle.properties 中保留下面两项过渡配置:
android.builtInKotlin=false
android.newDsl=false
它们用于退出 AGP 9 新增的默认行为。升级时应将这段配置与目标版本的工程差异一起检查,不要只更新插件版本号。React Native 的 AGP 9 接入建议
我会把构建结果按阶段保存:依赖解析、原生源码编译、资源处理、产物安装。假设本地能够生成调试包,但持续集成的发布包失败,优先比对的应是环境版本、构建变体和第三方插件输出。直接回到业务组件里改代码,通常会偏离报错发生的位置。
# 六、InteractionManager 的替换要保留业务语义
公告还包含若干接口清理,其中 InteractionManager 被移除,官方推荐使用 requestIdleCallback。但旧代码的用途值得先读一遍:它究竟只是推迟非紧急任务,还是依赖某个动画或交互完成的时机?接口移除清单
旧版InteractionManager 文档描述了等待交互结束的调度方式;requestIdleCallback 文档对应的是空闲任务调度。对明确依赖动画结束的业务,我建议接到所用动画库的完成回调,而不是用空闲时机间接猜测。
下面是可独立使用的非紧急任务队列示例。每个任务都应很短;回调检查剩余时间,在后续空闲时段继续处理: