用途
本技能用于在 Expo 应用中构建和检查导航与路由,确保路由结构与仓库内的路由契约(contracts/navigation.json)保持一致。它适用于使用 Expo Router 的项目,并附带一个针对 React Navigation 7 静态 API 的附录方案。
主要能力
- 强制单一 Expo Router 导航树,根布局 src/app/_layout.tsx 渲染根导航器,禁止同时引入 @react-navigation/* 或 NavigationContainer。
- 路由契约:每个路由文件(路由 id 为 src/app 下的路径去掉扩展名)必须出现在 contracts/navigation.json 的 routes 中,且契约中的每个路由也必须有对应文件;home 指定首个屏幕。
- 流程切换:使用 Stack.Protected 守卫组,由 store selector 驱动,禁止在布局中使用 <Redirect> 或 effect 中的 router.* 调用。
- 类型化 href:开启 experiments.typedRoutes,CI 通过 npx expo customize tsconfig.json 生成类型,代码中禁止使用 as Href。
- 参数解析:动态路由使用 valibot 解析器返回 Result,失败时显示 not-found 状态;参数保持为 id 和短纯值。
- 未保存内容保护:模型钩子调用 usePreventRemove(模板中的 useDiscardGuard),覆盖头部返回按钮、iOS 滑动、sheet 下滑、Android 返回按钮和预测性返回手势,以及 router.back()。
- 返回不叠加第二个 home:使用 router.dismissTo(home) 或 router.back()(<Link dismissTo>)。
- 模态框:作为具有 modal presentation 的路由,且必须在契约的 modals 中;确认框、丢弃对话框、暂停层渲染在屏幕或对话框宿主内。
- 未知 URL 落点:src/app/+not-found.tsx 渲染,唯一操作是 dismissTo('/'),不打印 URL。
- 原生堆栈:使用 expo-router 的 Stack(react-native-screens),动画来自 motion-and-animation 的 useScreenAnimation()。
- 导航逻辑位于屏幕的模型钩子中,src/ui、src/stores 等目录不导入 router。
- 调试路由受 TEST_ONLY 门控并置于受保护组中。
- 占位路由:未构建的路由渲染占位屏幕,契约中列出,--release 检查时若存在则失败。
- 提供检查脚本 scripts/check-navigation.mjs,支持 --release 模式,修复所有 FAIL 行直至 RESULT: PASS。
使用方式
- 阅读 references/expo-router-in-practice.md 中与任务对应的章节。
- 一次性设置:复制 templates/contracts/navigation.json 及其 schema,列出应用路由;复制 templates/src/app/+not-found.tsx 及 not-found 功能与 i18n 键;在 app.config.ts 中开启 experiments.typedRoutes;根布局和 AppStack 来自 app-startup-and-bootstrap 的根骨架,在 AppStack 内添加根 Stack.Protected 组。
- 添加路由:在契约中添加 id,创建薄路由文件渲染功能屏幕,仅在需要选项时在布局中添加 Stack.Screen,导航调用放在功能模型钩子中。
- 动态路由:参照 templates/src/features/task-detail/task-detail-params.ts 编写参数解析器及测试,在 paramParsers 中映射,解析失败时渲染 not-found 状态。
- 流程:用 <Stack.Protected guard={...}> 包裹屏幕,guard 来自 store selector,在 protected 中添加组,参照 protected-routes.test.tsx 测试。
- 未保存内容:复制 use-discard-guard.ts 及测试,在模型钩子中以 "unsaved" 布尔值调用,isPromptOpen 时渲染对话框,保存后通过 leave() 离开,并在 guarded 中列出路由。
- 运行测试:npx jest src/features/not-found src/features/root-navigator src/features/task-editor src/features/task-detail --selectProjects unit android,然后 npm run typecheck 和 lint。
- 运行检查器:node ${CLAUDE_SKILL_DIR}/scripts/check-navigation.mjs .,修复 FAIL 行直至 RESULT: PASS;发布前加 --release。
适用场景
- 添加新路由、链接、标签页或头部。
- 处理登录、引导、仅测试屏幕等受保护流程。
- 实现模态框、sheet、对话框。
- 处理返回、滑动、Android 返回、未保存更改。
- 编写路由测试。
限制
- 仅针对 Expo Router 项目,React Navigation 7 静态 API 为附录方案。
- 守卫无法感知进程被杀,需随更改保存草稿。
- 检查脚本需在项目根目录运行,发布前必须通过 --release 模式。