Expo Router:如何为每个Tab创建嵌套栈并实现动态路由?
Expo Router 实现 Tab 内嵌套栈的正确方式
等效实现逻辑说明
Expo Router 中对应 React Navigation 「每个Tab嵌套栈」的场景,核心是通过分组路由与嵌套布局文件来映射嵌套导航器的关系,官方文档的「嵌套路由」「底部Tab导航」章节覆盖了该实现逻辑,完全依赖目录结构声明路由层级。
解决重复渲染底部Tab的问题
你当前的目录结构错误导致了重复Tab,正确的结构需要把 chats 作为 (tabs) 的子目录,用 chats/_layout.tsx 定义Tab内部的栈导航,移除 [chat] 下多余的 _layout.tsx。
正确目录结构
app └── (tabs) ├── _layout.tsx # 底部Tab导航的根布局 ├── index.tsx # 首页Tab(可选) └── chats ├── _layout.tsx # Chats Tab 内部的栈导航布局 ├── index.tsx # 聊天列表页(对应路径 /chats) └── [chat] └── index.tsx # 消息历史页(对应路径 /chats/[chat])
关键代码配置
- (tabs)/_layout.tsx:定义底部Tab导航,将
chats作为其中一个Tab项
import { Tabs } from 'expo-router'; export default function TabsLayout() { return ( <Tabs> <Tabs.Screen name="index" options={{ title: '首页' }} /> <Tabs.Screen name="chats" options={{ title: '聊天' }} /> </Tabs> ); }
- chats/_layout.tsx:定义Chats Tab内部的栈导航,确保进入子页面时保留底部Tab
import { Stack } from 'expo-router'; export default function ChatsStackLayout() { return ( <Stack> <Stack.Screen name="index" options={{ title: '聊天列表' }} /> <Stack.Screen name="[chat]" options={{ title: '消息历史' }} /> </Stack> ); }
- chats/index.tsx:聊天列表页,通过Link跳转至动态路由
import { Link, View, Text } from 'expo-router'; export default function ChatsScreen() { return ( <View> <Link href="/chats/123"><Text>聊天会话123</Text></Link> <Link href="/chats/456"><Text>聊天会话456</Text></Link> </View> ); }
- chats/[chat]/index.tsx:消息历史页,获取动态路由参数
import { useLocalSearchParams, View, Text } from 'expo-router'; export default function ChatScreen() { const { chat } = useLocalSearchParams(); return <View><Text>当前聊天ID:{chat}</Text></View>; }
错误原因解析
你之前在 [chat] 目录下添加了 _layout.tsx,Expo Router会将其识别为独立的路由分支,从而额外渲染一套底部Tab。正确逻辑是:仅 (tabs) 下的 _layout.tsx 负责底部Tab,每个Tab内部的栈导航由该Tab目录下的 _layout.tsx 单独定义。
内容的提问来源于stack exchange,提问作者jolt
相关产品推荐
相关产品推荐

