规划Expo SDK 58升级:iOS 27、React Native 0.88以及新工具的使用方法
Expo SDK 58测试版的实用指南:iOS 27与React Native 0.88有哪些变化,哪些功能仍处于测试阶段,以及如何安全地进行升级测试。
来源: Expo SDK 58 Beta: What React Native Developers Actually Need to Know
Expo SDK 58 并非由某一项核心功能定义,而是一次性带来了大量更新:对 iOS 27 的支持、React Native 0.88 版本、更快速的 Android 构建速度、更低成本的本地模块调用成本、Android 主屏幕小部件功能、通过 App Intents 实现的 Siri 集成、专为编写代码代理而设计的命令行工具、更为成熟的 Expo Router,以及用于 Metro 的基于 Rust 的实验性转换器。本指南根据这些更新的实用影响对其进行分类,帮助你了解哪些变更会影响到你的代码库、哪些是无需额外成本的更新、哪些仍处于实验阶段,以及如何在不破坏现有应用的情况下完成升级。
将 SDK 58 视为测试版,因为它确实如此
在撰写本文时,SDK 58 仍处于测试阶段,Expo 预计这一阶段将持续大约三到四周。在规划任何与发布日期相关的事项之前,请先查看 Expo 的更新日志以了解当前状态。
测试阶段是检验应用程序代码、第三方库、自定义原生模块、构建流程以及任何手动编写的原生代码的理想时机。在周末前夕为生产环境的应用程序升级SDK并非明智之举。不过,对于业余项目及内部工具而言,利用测试阶段可以以较低风险预览未来功能,并在问题尚可由上游团队解决时及早发现。
iOS 27与基于场景的生命周期
SDK 58的开发很大程度上是受iOS 27的推动。基于iOS 27 SDK编译的应用程序会使用苹果的基于场景的生命周期机制,Expo也围绕这一模型对其原生基础设施进行了重构。
先介绍一下背景知识。在旧版本模型中,UIApplicationDelegate掌控着整个应用:一个窗口、一组启动和前台回调函数。场景模型则将这些职责分开,这样每个 UI 实例(即一个场景)都有独立的生命周期,由场景代理来管理。那些原本在应用代理中处理的任务,比如响应应用变为活跃状态或处理传入的 URL,现在可以移至场景级的回调函数中。
对于典型的 Expo 项目来说,这种转变应该是不可察觉的。但当你自定义了原生部分时,这种情况就会变得重要。一个常见的例子就是在应用代理的启动回调函数中添加额外设置,比如下面的 Swift 方法(此处未显示语法高亮):
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Custom initialization
return true
}
如果您的 AppDelegate.swift 包含大量自定义逻辑,其中部分逻辑可能应归属于场景处理而非单一的全局应用窗口。如果您修改了原生应用代理,Expo 建议查阅其关于场景生命周期迁移的指南。
哪些项目需要重点关注
许多 Expo 应用根本不会加载 AppDelegate.swift,因此这类应用的迁移应不会出现问题。不过,规模较大的应用随着时间推移往往会积累越来越多的原生代码,而这正是存在风险的地方。如果您的 iOS 项目包含以下任何功能的原生实现,请仔细检查:
- 分析 SDK 初始化
- 深度链接与通用链接处理
- 身份验证 SDK
- 推送通知
- 原生支付 SDK
这些通常都会与启动或 URL 回调函数相连,而正是这些回调函数会受到场景模型的影响。应明确测试这些流程,而非假设构建成功就意味着应用能够正常运行。
React Native 0.88与严格的 TypeScript API
SDK 58 测试版随 React Native 0.88 的候选版本一同发布。因此,升级 Expo 意味着要接受 React Native 0.87 和 0.88 中的所有变更,而不仅仅是某个单一版本的变更。
你最先会遇到的变化很可能是严格的 TypeScript API,它进一步限定了可视为 React Native 公共 API 的范围。那些导入到内部文件的代码将是主要受影响的部分:
import Something from 'react-native/Libraries/...';
多年来,应用程序和库一直使用这种深层导入方式,通常是因为公共入口点没有提供它们所需的功能。这种方式往往是偶然起作用的:在每次发布中,内部文件路径都可能未经通知就发生变动。现在React Native明确划分了公共模块与私有模块的界限,那些充斥着深层导入的代码库应当以SDK 58为节点开始清理这些导入。如果你的依赖树中的某个库存在此类问题,需检查是否有更新版本已经解决了该问题。
已被移除的API,如InteractionManager
也有一些较旧的API已被移除,InteractionManager就是需要排查的对象之一。那些将繁重任务推迟到动画和交互完成后再执行的代码通常如下所示:
InteractionManager.runAfterInteractions(() => {
loadHeavyContent();
});
推荐的替代方案是标准的空闲回调API:
requestIdleCallback(() => {
loadHeavyContent();
});
两者的含义相近但并不完全相同。InteractionManager会等待已注册的交互操作完成,而requestIdleCallback则会在JavaScript线程空闲时执行回调函数。对于大多数“屏幕显示后再加载此内容”的场景,这种差异并无影响,但如果你依赖它来等待特定动画完成,建议在真实设备上测试其行为。
对于小型应用,这种迁移通常很快完成。而对于存在大量依赖的旧应用,则应在专用分支上进行升级,并让TypeScript编译器列出所有可能出现的故障,然后再进行其他操作。这些编译错误就构成了你的迁移检查清单。
更快速的Android构建流程
expo-modules-core 现已内置针对 Android 的预编译原生库。此前,构建干净的 Android 版本时需要花费大量时间来编译 Expo 模块所依赖的 C++ 代码。SDK 58 则省去了大部分此类步骤。
根据 Expo 的测试,在 Apple M3 Pro 设备上运行一个空的 SDK 58 应用时,Android 版本的干净调试构建时间从大约 80 秒缩短到了约 39 秒。具体时间会因项目规模和硬件不同而有所差异,但由于完全省去了相关处理流程,因此任何使用 Expo 模块的项目都能获得同样的性能提升。
在持续集成环境中,这种优势会成倍放大。一个每天提交二十个拉取请求的团队,每个请求都会触发 Android 构建,每次构建都要支付编译成本,而且持续集成服务器的缓存往往处于初始状态。单次节省半分钟并不显著,但若在每次本地清理构建、每项持续集成任务、每个特性分支以及每次发布构建中都能节省时间,累积起来就会产生实际的时间和计算成本。构建速度也是一种性能表现形式,只不过它是以开发者的时间而非帧率来衡量的。
JavaScript 与原生代码之间的调用成本更低
SDK 58 中的性能优化并不仅限于构建过程。Expo 还降低了调用原生模块的成本,其中 iOS 系统的收益最为明显。长字符串的传输速度显著提升,异步原生函数返回的承诺对象也减少了额外开销。
您无需做任何更改即可受益:升级后,现有的 Expo 模块便会自动应用这些改进。
对于那些频繁在 JavaScript 与原生代码之间切换的应用而言,这一点尤为重要。请看这样的调用示例:
const result = await SomeNativeModule.processLargePayload(data);
原生函数或许能快速完成工作,但每次调用都需要将参数转换为原生类型,再将结果转换回 JavaScript 值,这都会产生一定的开销。如果一个应用需要数千次进行此类调用——比如在处理传感器数据流或同步大量数据时——每次切换带来的性能提升都会产生可观察的效果。
Expo 模块 2.0:使用原生类而非 DSL
对于那些需要自行编写原生模块的团队来说,Expo 模块 2.0 是最重要的变革之一。截至本文撰写时,它已在 iOS 和 Android 平台上处于测试阶段。
当前的 Expo Modules API 通过一种 DSL 来描述模块:即一个定义块,用于声明函数、属性和事件。而 Expo Modules 2.0 采用了更为传统的方式——你需要编写普通的 Swift 或 Kotlin 类,并为那些需要暴露给 JavaScript 的成员添加注解。
实际效果是,你的原生代码看起来依然像原生代码。Swift 保持其原有风格,Kotlin 也维持其特性,无需先学习框架特定的层结构。这降低了那些熟悉平台但不了解 Expo 的开发者的入门门槛,同时对 AI 编码工具也有帮助,因为它们的训练数据中包含的普通 Swift 和 Kotlin 代码远多于 Expo DSL 代码。
还有性能方面的考虑。绑定可以在构建时生成,而非在运行时动态解析,Expo的基准测试显示,在多个微基准测试中都能获得显著提升。微基准测试仅针对单一操作,因此很少能直接转化为应用层面的速度提升;不能仅凭此就重新设计原生架构。若想深入了解静态绑定为何更快,Nitro Modules:静态绑定为何优于React Native TurboModules一文中的对比也阐述了相同原理。Expo Modules 2.0的亮点在于它既让模块更易于编写,又提升了运行速度,这种优势组合实属难得。
Android上的主屏幕小部件
expo-widgets 现已支持 Android,为各类应用带来了新的产品创意:习惯追踪器、金融工具、配送应用、健身应用、任务管理器和日历应用。
习惯追踪类应用无需打开即可显示当天的进度。金融类应用则可以展示简短的预算汇总,标题可类似这样:
Monthly Budget
下方还会按类别列出剩余金额的明细:
₹34,500 remainingFood ₹8,200
Travel ₹3,400
Shopping ₹5,100
任务管理类应用可以直接在主屏幕上显示当天最重要的三项任务。
在 Android 上,每个小部件都在独立的 Hermes 运行时中运行自己的 JavaScript 包,能够处理用户交互,并可使用 Material Colors 以与系统主题相融合。这种独立的运行时值得了解:小部件不会与你正在运行的应用共享内存或状态,因此必须显式地将数据传递给它,而不能从应用内部存储中读取。对于跨平台团队而言,在 Expo 生态系统中为两个平台都提供小部件,进一步减少了维护独立原生实现的理由。
应用意图:Siri、Shortcuts 与 Spotlight
expo-app-intents 可将 Expo 应用与苹果的 App Intents 系统相连,该系统是多种系统界面的底层机制:
- Siri
- Shortcuts
- Spotlight
- Apple Intelligence
想象一下任务管理器。通常,完成一项任务意味着打开应用、找到对应项并点击它。而当意图被公开后,用户只需对Siri说:
“完成上传ID文档。”
Siri会找到匹配的任务并调用你的应用所声明的操作。你的JavaScript代码会接收到该调用并更新应用的数据。在Expo的示例中,这一切无需将应用带到前台即可实现。
这更广泛地意味着移动应用的架构正在发生变化。应用图标不再是最唯一的入口点。有些操作来自Siri,有些来自小部件,有些来自Spotlight搜索,还有些来自用户的Shortcuts自动化功能。应用变成了一组能力,操作系统可以在任何需要的地方调用这些能力,设计功能时这一点值得考虑。
一个重要的注意事项是:在撰写本文时,expo-app-intents仍处于测试阶段。可以尝试使用它,但暂勿将其用于关键的业务流程中。
Expo UI 更接近 SwiftUI 和 Jetpack Compose
Expo UI 不断加深与 SwiftUI 和 Jetpack Compose 的集成。SDK 58为 iOS 添加了SwiftUI导航相关功能:
NavigationStack
NavigationLink
NavigationDestination
Toolbar
这些功能与SwiftUI自身的导航组件相对应,因此你能获得原生的堆栈导航和工具栏,而非通过JavaScript重新实现的版本。此次更新还新增了更多Jetpack Compose组件,并优化了那些可在多平台使用的通用组件。还有一个看似微小的改进却非常实用:现在Expo UI文档中的每个组件都配有可视化示例。没有截图的话,选择UI组件往往会导致令人沮丧的反复尝试过程:
- 找到音效合适的组件。
- 阅读其属性说明。
- 将其连接到界面中。
- 启动应用程序。
- 发现实际效果与想象大相径庭。
- 换用其他组件重新开始。
将渲染后的组件与其API一同展示,就能打破这种循环。
Device Hub与基于浏览器的模拟器控制
Xcode 27用Device Hub取代了Simulator.app,Expo CLI也支持这种新工作流程。此外,Expo还在测试一个独立的包expo-device-hub,它能在浏览器中为模拟器和仿真器提供本地控制面板。
通过该控制面板,你可以查看:
- 设备屏幕的实时画面
- CPU和内存使用情况
- 网络流量数据
- 浅色/深色模式等外观选项
- 无障碍功能设置
你还可以在浏览器中通过点击、滑动和输入来操作设备。要尝试一下,只需安装相关包并启动开发服务器即可:
npx expo install expo-device-hub
npx expo start
如果你的日常工作需要在编辑器、终端、Android模拟器、iOS模拟器以及各种性能分析工具之间频繁切换,将其中部分功能整合到同一个浏览器标签页中,就能显著提升开发效率。
Expo还在开发EAS Simulator,该工具旨在通过云托管的模拟器提供类似的功能,这样就不必在本地安装Xcode或Android Studio了。
专为编码代理设计的命令行界面
这一新增功能清楚地展现了开发者工具的发展方向。Expo已经发布了一个实验性包:
@expo/agent-cli
编码工具在需要决定使用哪种工具时往往会遇到困难:Expo CLI、EAS CLI、expo-doctor 等,每种工具都有各自的参数和输出格式。该代理 CLI 将这些工具统一在一个一致的接口下。为项目配置它的方法如下:
npx @expo/agent-cli@latest agents:setup
其中一些命令可以展示其功能。第一个命令是:
status
它可以在不启动应用程序的情况下检查诸如 Expo Go 兼容性之类的问题。下一个命令是:
dev
它启动开发环境,并尝试找到最快的方式让应用程序运行起来。最后一个命令是:
smoke
它启动应用程序,截取屏幕截图,然后再将其关闭。
最后一个命令对自动化工作流而言最为重要。假设你让智能体添加一个设置界面并确认应用仍能正常启动。智能体无需直接假设修改有效,而是可以运行测试用例并查看结果。这标志着智能体从单纯的代码生成工具向能够自我验证工作的工具迈出了小小一步,而这也正是它们具备实际价值的关键所在。该功能仍处于实验阶段,因此其命令可能会发生变化。
Expo Router稳定多项功能
SDK 58为Expo Router带来了重大更新,此前处于实验阶段的几项功能现已稳定:
- 数据加载器,用于在界面渲染前获取路由数据
- 用于网页输出的服务器端渲染功能
- 在请求到达路由之前运行的中间件
- 由平台自带标签栏支持的原生标签页
- 工具栏功能
在底层,导航逻辑也经过了重写,使得导航状态更具确定性,即相同的操作序列总能产生相同的状态。大多数应用应该不会察觉到差异。如果您自定义了导航器或直接从以下路径导入代码,请仔细阅读迁移指南:
expo-router/react-navigation
在网页应用中,异步路由现已成为默认选项。每个路由都可以拆分为独立的 JavaScript 模块,在用户访问该页面时再加载,从而无需在首次加载时就传输所有页面的代码。对于基于 Expo Router 构建的网页应用而言,这是合理的默认设置,因为初始加载时间通常是用户最在意的方面。需要注意的是,这也意味着路由在首次访问时可能会短暂加载,因此需要仔细检查网页上的加载状态。
用于 Metro 的实验性 Rust 转换器
从技术层面来看,这或许是此次版本中最有趣的变化,尽管它仍属于实验性质。Expo正在测试一种用Rust编写的JavaScript转换工具Noxcturnal,该工具基于oxc工具链构建。
Metro的冷打包过程中,很大一部分时间都用于Babel转换:解析每个文件、应用插件并生成输出。用原生Rust代码替代这一步骤可直接解决最耗时的部分。在Expo的测试中,单个模块的转换速度提升了约三倍,而整个冷打包的速度则提升了近两倍。一个包含大约5,500个文件的大型测试应用,打包时间从5.26秒缩短到了2.46秒。
你可以通过配置中的实验标志来启用此功能:
experiments: {
noxcturnalTransformWorker: true
}
在启用该功能之前请先了解其限制。那些依赖自定义 Babel 插件、预设或专用转换工具的项目仍可能回退到使用 Babel,因此性能提升程度取决于您的配置标准如何。由于该功能仍处于实验阶段,其行为和配置在不同版本之间可能会发生变化。
这一发展方向与更广泛的生态系统保持一致,多年来,那些对性能要求极高的 JavaScript 工具一直在从 JavaScript 转向 Rust。Expo 现在也在为 Metro 探索同样的路径。
减少不必要的原生代码重新编译
SDK 58 中的默认指纹生成模式现为 balanced 预设。指纹是对所有影响原生代码编译的元素的哈希值;一旦该值发生变化,Expo 的工具就会认为需要新的原生二进制文件。
乍听之下这似乎没什么用,但它能避免因那些并未影响原生代码的修改而不得不重新构建原生版本。在某些配置下,修改app.json中的应用版本之类的内容曾经会导致指纹变化。平衡预设会尝试忽略那些不会真正改变原生运行时的改动。实际效果很简单:无需无故触发重新构建,这也意味着在依靠指纹来判断是进行空中更新还是下载新版本时,不必要的二进制文件构建也会减少。虽然没人会演示这类功能,但所有人都能从中受益。
值得了解的细微改进
除了上述重点功能外,根据您的应用不同,还有几项模块更新可能具有相关性:
expo-camera增加了多页文档扫描功能,适用于收据扫描、文档上传流程、身份验证(KYC)以及费用管理。expo-font在Android、iOS和网页上均支持可变字体。expo-audio新增了基于播放列表的锁屏控制功能以及WAV/PCM格式录音支持。expo-file-system增加了更多哈希算法接口以及原生预览功能。expo-notifications新增了诸如在Android上发送闹钟通知等功能。- 通过与PostHog集成,可将EAS构建和更新信息附加到分析事件中。
这些功能单独来看都无法定义一次版本发布,但综合起来却体现了该平台的功能覆盖范围之广。
升级前需审查的重大变更
在应用停止构建之前,升级过程一直都很令人兴奋,因此千万不要跳过这一步。在将实际项目迁移到 SDK 58 之前,请先检查以下内容。
React Native 深层导入
在代码库中搜索以下路径前缀:
react-native/Libraries/
每一个匹配项都是 React Native 内部文件的导入,而严格的 TypeScript API 可能会拒绝这些导入。同时也要检查 node_modules,或者至少查看那些出现问题的依赖项,因为库往往是问题来源。
已被移除的 React Native API
确认您的应用及其依赖项均未使用在 React Native 0.87 和 0.88 版本中被移除的 API,例如 InteractionManager。
自定义 AppDelegate 代码
如果 iOS 项目包含针对原生生命周期的自定义处理,需根据 iOS 27 所要求的基于场景的生命周期进行审查。
Expo Router 中的自定义导航
自定义导航组件以及从 expo-router/react-navigation 导入的组件都会受到内部结构调整的影响;请遵循迁移指南操作。
文件系统写入
File.write() 现在是异步操作。那些假设下一行代码执行时文件已存在的代码,除非等待该调用完成,否则将会出错。如果确实需要同步行为,请改用:
File.writeSync()
Node.js 版本
Expo 支持的 Node.js 版本已升级。在认定构建失败是 Expo 的缺陷之前,请先检查本地环境及 CI 环境中的版本情况。
安全升级测试方法
不要直接在主生产分支上进行操作。请创建一个专用的迁移分支,然后在其中执行升级:
npx expo install expo@next --fix
expo@next 标签会安装测试版,而 --fix 选项则会让其他 Expo 包的版本与它兼容。
首先测试那些不那么引人注目的流程,因为当它们出问题时用户最先会注意到:
- 应用能否正常启动?
- 登录和认证功能是否正常?
- 用户能否在主界面之间切换?
- 深度链接是否能跳转到正确页面?
- 推送通知是否能够送达?
- 用户能否上传图片和文件?
- 支付功能是否能够完成?
- Android版本构建是否成功?
- iOS版本构建是否成功?
只有这些测试都通过后,才有必要去探索新功能。尝试新的API固然有趣,但回归测试才是让用户满意的关键。
Expo的发展方向
从SDK 58的整体来看,发展方向比任何单一功能都更为重要。Expo最初是为了让React Native更易于使用而诞生的,如今它正逐渐成为一个完整的应用平台:
- Expo Router,用于应用架构与导航
- Expo Modules,用于调用原生API
- EAS Build,用于应用构建
- EAS Update,用于发布更新
- EAS Observe,用于生产环境监控
- Expo UI,用于原生界面组件
- widgets与App Intents,用于操作系统集成
- agent工具,用于人工智能辅助开发
除此之外,Expo还在持续投入开发者日常工作中会用到的基础设施:更快的构建速度、更低成本的原生调用费用、更少的重新构建次数、更优的隧道连接、更好的模拟器工具,以及可能的更快打包速度。这些或许无法生成令人惊叹的截图,但却能提升日常工作的效率。如需更全面了解Expo的定位,请参阅为何Shopify退出React Native业务不会威胁Expo。
现在应该升级吗?
对于生产环境的应用,应在提交代码前进行测试。SDK 58仍处于测试阶段,React Native 0.88仅为候选版本,部分新API仍是实验性或测试版状态,在正式发布前还会有更多修复工作。
对于个人项目、内部工具或迁移分支,现在进行尝试是非常值得的,尤其是当你需要支持iOS 27或希望提前适应React Native的变更时。在测试阶段发现的问题的确可以在正式发布前上报并得到解决,这比等所有人都升级后再发现问题要容易得多。
需要记住的命令就是你开始使用时用的那个:
npx expo install expo@next --fix
在分支上运行它,而非在生产环境中。
关键要点
- 最可能破坏升级体验的变更包括深度导入、已被移除的 API 如
InteractionManager、自定义的 代码以及自制的 Expo Router 导航组件。 - 升级后还能免费获得更快的 Android 构建速度、更低成本的本地调用开销,以及因设备指纹变化而触发的重建次数减少。
- Expo Modules 2.0、agent CLI、Device Hub 以及 Noxcturnal 转换工具目前仍处于实验或测试阶段;可进行试用,但暂不宜依赖它们。
expo-app-intents为测试版,而 Android 小部件则让跨平台团队能够实现更丰富的主屏幕功能。- 请在专用分支上执行升级,先测试关键的用户流程,只有在这些流程通过验证后才能尝试新功能。