Skip to content
雲里
里雾

Expo Router 深度解析 — 文件路由与 RN 导航

mindgym 开发 更新于 2026/4/12

Expo Router 是 React Native 的文件路由方案,建在 React Navigation 之上。它把 Next.js App Router 的”文件系统 = 路由结构”思路带到了移动端,解决了 React Navigation 手动配置路由时的发现性差、类型不安全、deep linking 配置繁琐三个问题。


概述

移动端导航为什么比 Web 复杂

Web 的导航本质是 URL → 页面的映射,浏览器管理历史栈,<a href> 就够了。移动端多出三层复杂度:

  1. 页面生命周期:页面在导航后仍保留在内存中(iOS 的 UINavigationController),返回时状态完好。导航框架要管理一棵活的页面树。
  2. 多范式并存:Stack(堆叠)、Tabs(底部切换)、Drawer(侧滑)、Modal(弹窗)同时存在且可嵌套。
  3. 原生过渡动画:iOS 从右滑入,Android 从底部渐入,JS 导航框架要模拟或桥接这些原生行为。

RN 导航方案演变

时期方案解决的问题引入的新问题
2015Navigator (RN 内置)基础导航能力iOS/Android API 不统一,纯 JS 动画卡顿
2017React Navigation统一跨平台 API,声明式配置手动维护路由树,导航目标是”魔法字符串”
2023Expo Router文件路由 + Typed Routes + 自动 deep linking受文件系统约束,灵活性低于直接使用 React Navigation

Expo Router 不是 React Navigation 的替代品——底层的 Stack/Tab/Drawer 容器、手势处理、过渡动画全部由 React Navigation 提供。Expo Router 只解决上层的开发体验:路由发现、类型安全、deep linking 配置。


工作方式

文件路由映射规则

app/ 目录下的文件结构就是路由结构:

文件路径URL 路径说明
app/index.tsx/根路由
app/(tabs)/index.tsx/(tabs) 是分组,不影响 URL
app/(tabs)/history/index.tsx/history嵌套目录的 index
app/train/[module].tsx/train/schulte动态路由,方括号里是参数名
app/train/result.tsx/train/result静态路由
app/_layout.tsx(不映射)Layout 文件,不是路由

核心规则:

_layout.tsx:包装层而非路由

_layout.tsx 定义”这个目录下的路由应该被包装在什么容器里”。两个职责:

  1. 定义导航容器:Stack(页面堆叠)、Tabs(底部切换)、Drawer(侧滑)
  2. 注入 Provider/全局 UI:语言 Context、主题 Provider、StatusBar 等

嵌套 layout 形成组件树:

RootLayout (Stack)
├── TabLayout (Tabs)
│   ├── 首页
│   ├── 历史
│   └── 设置
└── TrainLayout (Stack)
    ├── 训练页 [module]
    └── 结果页

和 Next.js App Router 的 layout 概念一致。区别:Next.js 用 {children} 渲染子路由,Expo Router 用 <Stack>/<Tabs> 导航组件——因为移动端需要导航容器管理页面堆栈和过渡动画。

(group) 分组语法

括号目录名不出现在 URL 路径里,纯粹用于文件组织。典型用途:

(auth) + (app) 可以实现登录态路由拆分,而 URL 都从根开始,没有多余的 /auth/ 前缀。

Typed Routes

app.json 中开启 "typedRoutes": true,Expo 自动扫描 app/ 目录生成 .expo/types/router.d.ts。原理:通过 TypeScript 的 module augmentation 扩展 expo-router 模块,把实际路由路径注入 href 类型联合。

router.push('/train/schulte')   // OK — 匹配 /train/${string}
router.push('/trian/schulte')   // 类型错误 — 拼写错误在编译期发现

实现层面:

router.push vs router.replace

判断标准:用户按返回按钮时,回到上一个页面是否有意义?

useLocalSearchParams vs useGlobalSearchParams

路由参数本质是 URL query string,只能传 string | number,不能传对象或数组。


与 React Navigation 对比

维度React NavigationExpo Router
路由发现需要读配置代码app/ 目录结构
类型安全手动维护 ParamList 类型自动生成 Typed Routes
Deep Linking需手动配置 linking 对象文件结构即 URL,自动支持
灵活性任意组合 Navigator受文件系统约束
Web 支持有限一等公民
底层自己就是底层建在 React Navigation 之上

React Navigation 更好的场景

  1. 极复杂的动态路由树:根据用户角色动态插入不同子路由,文件路由的约定很别扭
  2. 不使用 Expo:Expo Router 依赖 Expo 构建管道
  3. 渐进式迁移:大型项目迁移需要一次性重组 app/ 目录,React Navigation 可以逐步改
  4. 非标准导航模式:同时显示两个 Stack、Tab 间拖拽切换等

参见


参考

  1. Expo Router 官方文档 — 最权威参考,特别是 File-based Routing 和 Layouts 两节
  2. Evan Bacon: Why Expo Router — 作者解释设计动机
  3. React Navigation 官方文档 — 理解 Expo Router 的底层,特别是 Native Stack Navigator 和 Bottom Tabs Navigator