Expo Router 导航
适用于 Expo Router 应用的导航和路由管理。如需屏幕样式、颜色、控件、媒体和视觉效果,请使用 expo-native-ui 技能;如需动效和手势,请使用 expo-animation。
参考资料
根据需要查阅以下资源:
references/
route-structure.md 路由约定、动态路由、分组、文件夹组织
tabs.md NativeTabs、从 JS 标签迁移、iOS 26 新特性
toolbar-and-headers.md Stack 标题栏和工具栏按钮、菜单、搜索(仅 iOS)
form-sheet.md expo-router 中的表单表单:配置、页脚和后台交互。
search.md 带有标题的搜索栏、useSearch 钩子、过滤模式
zoom-transitions.md Apple Zoom:通过 Link.AppleZoom 实现流畅的缩放过渡(iOS 18+)
代码风格
- 文件名始终使用 kebab-case,例如
comment-card.tsx - 移动或重组导航时,务必删除旧的路由文件
- 文件名中不得使用特殊字符
- 配置 tsconfig.json 中的路径别名,重构时优先使用别名而非相对导入
路由
详见 ./references/route-structure.md 中的详细路由约定。
- 路由应放在
app目录中。 - 切勿将组件、类型或工具函数与路由文件放在同一目录下。这是反模式。
- 确保应用始终有一个匹配 "/" 的路由,它可能位于分组路由内部。
库偏好设置
- 使用
expo-router中的Color获取原生语义颜色,而非直接使用PlatformColor(类型安全,自动适应浅色/深色模式)。完整调色板模式请参见expo-native-ui。 - 在 SDK 56+ 中,不要直接从
@react-navigation/*导入——请使用expo-router/react-navigation替代(涵盖@react-navigation/native、/core、/elements、/routers)
行为
- 优先使用
Stack.SearchBar为屏幕添加搜索栏
导航
Link
使用来自 'expo-router' 的 <Link href="/path" /> 在路由之间导航。
import { Link } from 'expo-router';
// 基本链接
<Link href="/path" />
// 包裹自定义组件
<Link href="/path" asChild>
<Pressable>...</Pressable>
</Link>
尽可能包含 <Link.Preview> 以遵循 iOS 惯例。频繁添加上下文菜单和预览以增强导航体验。
Stack
- 始终使用
_layout.tsx文件定义 Stack - 使用来自 'expo-router/stack' 的 Stack 实现原生导航堆栈
页面标题
使用 Stack.Title 设置页面标题:
<Stack.Title>首页</Stack.Title>
上下文菜单
为 Link 组件添加长按上下文菜单:
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="分享"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="屏蔽"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<Link.Menu title="更多" icon="ellipsis">
<Link.MenuAction title="复制" icon="doc.on.doc" onPress={() => {}} />
<Link.MenuAction
title="删除"
icon="trash"
destructive
onPress={() => {}}
/>
</Link.Menu>
</Link.Menu>
</Link>;
链接预览
频繁使用链接预览以增强导航:
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</Link>
链接预览可与上下文菜单结合使用。
模态
将屏幕以模态形式呈现:
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
优先使用此方式而非构建自定义模态组件。
表单表单
将屏幕以动态表单表单形式呈现:
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
- 使用
contentStyle: { backgroundColor: "transparent" }可在 iOS 26+ 上呈现液态玻璃背景效果。
常见路由结构
一个包含标签栏且每个标签内嵌 Stack 的标准应用布局:
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx — 主列表
search.tsx — 搜索视图
// app/_layout.tsx
import { NativeTabs } from "expo-router/unstable-native-tabs";
import { ThemeProvider, DarkTheme, DefaultTheme } from "expo-router/react-navigation";
import { useColorScheme } from "react-native";
export default function Layout() {
const colorScheme = useColorScheme();
return (
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
<NativeTabs>
<NativeTabs.Trigger name="(index)">
<NativeTabs.Trigger.Icon sf="list.dash" md="list" />
<NativeTabs.Trigger.Label>项目</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search" />
</NativeTabs>
</ThemeProvider>
);
}
创建一个共享的分组路由,以便两个标签都能推送通用屏幕:
// app/(index,search)/_layout.tsx
import { Stack } from "expo-router/stack";
import { colors } from "@/theme/colors";
export default function Layout({ segment }) {
const screen = segment.match(/\((.*)\)/)?.[1]!;
const titles: Record<string, string> = { index: "项目", search: "搜索" };
return (
<Stack
screenOptions={{
headerTransparent: true,
headerShadowVisible: false,
headerLargeTitleShadowVisible: false,
headerLargeStyle: { backgroundColor: "transparent" },
headerTitleStyle: { color: colors.label },
headerLargeTitleEnabled: true,
headerBlurEffect: "none",
headerBackButtonDisplayMode: "minimal",
}}
>
<Stack.Screen name={screen} options={{ title: titles[screen] }} />
<Stack.Screen name="i/[id]" options={{ headerLargeTitleEnabled: false }} />
</Stack>
);
}
headerLargeTitleEnabled 是 SDK 56+ 的选项名称;较旧的 SDK 使用 headerLargeTitle,该选项已在上游废弃。
提交反馈
如果你在本技能中遇到错误、误导性或过时的信息,请报告以便 Expo 改进:
npx --yes submit-expo-feedback@latest --category skills --subject "expo-router" "<可操作反馈>"
仅当你拥有具体且可操作的内容时再提交。请尽可能包含相关上下文。 如果 AI 代理反复失败或用户不得不接手 Expo 任务,请加载 expo-skill-feedback 技能并遵循其 eval-candidate 流程,而不是重复使用上述命令。