Expo Router v56:与 React Navigation 解耦
Expo Router v56 从 React Navigation 分叉,提供流式 SSR,并通过新的 Android 工具栏和 Native Tabs 更新缩小平台差距。
中文
复制

Expo Router v56 是该库发布以来最大的一次变革。我们 fork 了 Expo Router 所依赖的 React Navigation 中的部分代码,推出了流式服务端渲染,并通过新的 toolbar API 和扩展后的 Native Tabs,让 Android 的功能更接近 iOS。
fork 是这次最核心的变化,值得先解释,因为它影响到每一个 Expo Router 应用。之后我们会介绍新的 SSR API、Android toolbar,以及 Native Tabs 的新内容。
我们为什么决定 fork React Navigation?
Expo 与 React Navigation 渊源已久。Brent Vatne 是 Expo 任职时间最长的员工,也是我们的工程副总裁,他带领项目走过了 1.0 和 2.0 版本,与 @ericvicenti 和 @satya164 并肩合作。React Navigation 最初只是一个早期想法,受 React Native 旧的 NavigationExperimental API 和 Expo 自家的 ex-navigation 影响,Expo 帮助它成长为 React Native 生态的标准导航库。如今,它依然是生态中核心的导航方案之一,与 Expo Router 并列。
React Navigation 现在由 Satya 全权负责,而我们在导航领域的重心已越来越集中在 Expo Router 上。随着 Expo Router 的发展,我们发现自己需要对 Expo Router 所依赖的 React Navigation 内部实现有更深入的控制。我们与 Satya 沟通过,双方一致认为,对两个项目来说最好的出路是让 Expo Router fork 它最直接依赖的那部分 React Navigation 代码。
这并不是要远离 React Navigation 这个项目,而是让两个项目都能自由地朝着最有利于各自用户的方向演进。
对 Expo Router 来说,fork 让我们有空间去做针对文件路由、Web、服务端渲染以及其他 Expo Router 使用场景的改动。我们想做的一些改进未必直接适用于 React Navigation 用户,把这些改动推到上游只会给所有人增加复杂度。
这也让我们能够为 Expo Router 内部直接使用而打造一个更精简的 React Navigation 内部实现。掌控这一层之后,我们可以简化集成、减少绕行代码,让 Expo Router 长期更易维护。
另一个现实原因是依赖版本管理。过去的版本中,项目里可能同时装上多个版本的 React Navigation,由此引发各种问题。把我们依赖的内部模块 fork 出来,能让我们更好地掌控兼容性,也让 Expo Router 的发布更可预测。
最后,Expo Router 正在增强其 Web 能力,比如数据加载器和服务器端渲染。这些功能并不属于 React Navigation,要好好支持它们,就得做不少改动——而在一个专为 Expo Router 定制的代码库里做这些改动要容易得多。
这对你意味着什么?
由于 Expo Router 不再依赖 React Navigation,你将无法再使用直接从 @react-navigation/* 导入的代码。我们知道这对你来说是一个破坏性变更,因此准备了一些工具来简化迁移。
要把代码库里所有 React Navigation 的导入迁移到 expo-router/react-navigation,可以使用我们的 codemod:
px expo-codemod sdk-56-expo-router-react-navigation-replace src
如果你决定手动迁移,我们的迁移指南包含了所有必要步骤。最后,为了让迁移循序渐进、平稳过渡,从各库中导入 @react-navigation/core 的代码至少在一个发布周期内会自动替换为 expo-router/react-navigation 的版本。
如果你遇到任何问题或发现任何异常,请告诉我们!
Expo Router 接下来会怎样?
我们正与 React Navigation 团队合作,为库作者设计一套共享 API,以同时支持两个库。
如果你维护的库需要同时支持 Expo Router 和 React Navigation,欢迎联系我们,一起把它适配到新的 API 上。
Expo Router 中的 Suspense 自定义
我们已支持在 _layout 内自定义路由的 Suspense fallback。你可以导出一个 SuspenseFallback来自定义加载界面。
import { ActivityIndicator, View } from 'react-native';
import { Stack } from 'expo-router';
export function SuspenseFallback() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" />
</View>
);
}
export default function Layout() {
return <Stack />;
}
Expo Router 的服务端渲染
除了已有的服务端功能(包括 API routes、middleware、data loaders 和 static rendering),本次发布还引入了流式服务端渲染。它让你可以把关键 UI 元素排在前面,先于那些依赖数据、加载较慢的 UI 呈现,从而改善用户的感知性能:
视频:Expo Router 的服务端渲染——在 expo.dev 上观看
用法很简单:在 app config 的 Expo Router 配置项里把 unstable_useServerRendering 设为 true 即可。
作为流式 SSR 的一部分,我们还引入了为路由生成 HTML 元数据的新方式:使用 generateMetadata 函数。
我们还新增了一些辅助工具,让编写类型安全的 data loader 更容易:createStaticLoader、createServerLoader。
Android 工具栏
在 SDK 55 中,我们为 iOS 加入了工具栏支持。本次发布中,我们在 Android 上加入了对应的功能,实现了跨平台兼容。和 iOS 一样,Android 工具栏也有三个位置可选:左侧、右侧和底部。

查看工具栏中所有可用选项的文档。
原生标签页
去年秋天,在 Router v6 中(此后我们更改了 Router 的命名方式),我们加入了对原生标签页组件的支持。我们正在积极推进对所有平台的完整支持,目标是在下个版本中将该 API 标记为稳定。
Expo Router v56 为原生标签页增加了若干新选项。呼声最高的功能之一,是阻止标签被选中。从这个版本开始,你可以用 disabled 属性让标签显示在标签栏中,但不允许用户点击它。
视频:Expo Router v56 为原生标签页增加了若干新选项——在 expo.dev 上观看
所有可用选项和各平台支持情况见原生标签页参考页面。
如何开始使用 Expo Router v56
如果这篇文章你只记住一件事,我们希望是这三个更新让你感到兴奋:
-
React Navigation 分叉意味着每个 Expo Router 应用只需更新一次导入。我们提供了 codemod 和兼容层,让迁移尽可能简单。
-
流式 SSR、
generateMetadata以及新的 loader 辅助函数,帮你构建更好的 Expo Router for Web 体验。 -
Android 现在支持工具栏,原生标签页 API 也有所扩展,与 iOS 看齐。
如果你要升级现有应用,请先看迁移指南,然后运行 codemod。如果你要新建项目,SDK 56 默认就包含这一切。