Ignite 中的导航体系:基于 React Navigation v7 的 AppNavigator 架构与实战模式

Ignite 中的导航体系:基于 React Navigation v7 的 AppNavigator 架构与实战模式 Ignite 中的导航体系基于 React Navigation v7 的 AppNavigator 架构与实战模式【免费下载链接】igniteInfinite Reds battle-tested React Native project boilerplate, along with a CLI, component/model generators, and more! 9 years of continuous development and counting.项目地址: https://gitcode.com/GitHub_Trending/ig/ignite这篇指南聚焦 Ignite 样板工程boilerplate/的导航体系它以 React Navigation v7 为核心通过根导航器AppNavigator、一套导航工具函数navigationUtilities.ts以及认证流程、Tab 导航、抽屉侧边栏三大实战模式覆盖了绝大多数 React Native 应用的路由需求。读完本文你将掌握如何组织 Ignite 项目的导航结构、如何用条件渲染实现认证态切换、如何配置底部 Tab 与嵌套 Stack以及如何借助导航持久化与 Android 返回键处理等工具函数提升开发效率。React Navigation v7 与 Ignite 的导航目录结构当前版本的 Ignite 使用 React Navigation v7 是应用的主导航器根导航器。在该目录下Ignite 将导航相关的职责拆成了三个文件各司其职文件职责AppNavigator.tsx根导航器包含NavigationContainer与AppStack原生栈导航器navigationTypes.ts集中定义所有路由的 ParamList 类型供类型安全的navigate调用使用navigationUtilities.ts提供getActiveRouteName、useBackButtonHandler、useNavigationPersistence等工具函数navigationTypes.ts是导航类型体系的中心navigationTypes.ts 中定义了应用级路由表AppStackParamList和演示 Tab 的路由表DemoTabParamList并派生出了AppStackScreenProps栈屏 props与DemoTabScreenProps组合屏 props两个便捷类型。组合类型CompositeScreenProps让处于 Tab 内部的屏幕既能使用 Tab 导航器的类型也能感知外层 Stack 的类型从而获得完整的编译期类型检查。navigationTypes.ts中还保留了生成器锚点注释// IGNITE_GENERATOR_ANCHOR_APP_STACK_PARAM_LISTIgnite 的 CLI 生成器会把新生成的屏幕类型自动插入到这个位置保证新增屏幕时类型定义与导航器保持同步。AppNavigator 的通用结构Ignite 推荐在根组件中按如下方式挂载导航器将导航状态的初始化与变更交给AppNavigator处理AppNavigator initialState{initialNavigationState} onStateChange{onNavigationStateChange} /其中initialState用于在启动时恢复上次的导航状态onStateChange用于在导航状态变化时回调例如持久化到本地存储。二者的数据源来自useNavigationPersistence工具函数下文详述。查看 AppNavigator.tsx 的源码实现可以看到AppNavigator组件本身做了三件关键的事注册导航容器引用通过ref{navigationRef}把NavigationContainer绑定到全局的navigationRef上使得任何非组件代码如 Redux/MobX action、普通工具模块都能借助navigate()、goBack()等工具函数发起导航而不必层层透传navigationprop接入主题从useAppTheme()中取出navigationTheme让导航器的背景色、文字颜色与应用的明暗主题保持一致包裹错误边界用 ErrorBoundary 包裹整个AppStack并按Config.catchErrors的配置决定在哪些环境捕获渲染错误。AppStack本身是一个由createNativeStackNavigator创建的原生栈导航器AppNavigator.tsx它的screenOptions统一设置了headerShown: false隐藏系统头部由各屏幕自行定制、navigationBarColorAndroid 导航栏颜色与contentStyle栈内容背景色三者都取自主题色保证深浅色模式下视觉一致Stack.Navigator screenOptions{{ headerShown: false, navigationBarColor: colors.background, contentStyle: { backgroundColor: colors.background, }, }} initialRouteName{isAuthenticated ? Welcome : Login} 关于根导航器与AppStack的更细致说明可继续阅读 AppNavigator.tsx.md。实战模式一认证流程Authentication FlowIgnite 推荐遵循 React Navigation 官方的 Authentication Flows 思路根据登录态条件渲染不同的屏幕集合。这种方式比手动reset导航栈更简洁且状态变化时 React Navigation 会自动处理栈的替换。Ignite 的 demo 代码自带了这一模式的完整实现。核心代码如下见 AppNavigator.tsxconst AppStack () { const { isAuthenticated } useAuth() return ( Stack.Navigator screenOptions{{ headerShown: false }} initialRouteName{isAuthenticated ? Welcome : Login} {isAuthenticated ? ( Stack.Screen nameWelcome component{WelcomeScreen} / Stack.Screen nameDemo component{DemoNavigator} / / ) : ( Stack.Screen nameLogin component{LoginScreen} / / )} /Stack.Navigator ) }AppStack中是否包含某个屏幕完全取决于isAuthenticated的值用户未认证时导航器中只有LoginScreen其余屏幕全部不在栈内用户无法通过导航访问受保护页面用户已认证时LoginScreen被移出导航器用户看到的是WelcomeScreen以及DemoNavigator之下的所有演示屏幕。这种未认证的屏幕根本不存在于导航树中的设计从结构上杜绝了绕过登录访问受保护页面的可能。在 Ignite 的 demo 中isAuthenticated来自 AuthContext.tsx 暴露的useAuth()hook。其实现基于react-native-mmkv的useMMKVString持久化 tokenisAuthenticated即!!authToken登录成功写入 token 后组件重新渲染导航器自动切换屏幕集合logout()清空 token 与邮箱后用户便回到登录页。这一整套 Provider 由根组件app.tsx中的AuthProvider挂载。值得注意的是demo 相关的代码行都带有demo remove-current-line/demo remove-block-start注释运行npx ignite-cli remove-demo时这些代码会被自动剥离导航器会回归纯净的初始结构。实战模式二底部 Tab 导航对于底部 TabIgnite 推荐直接使用 React Navigation Tabs。Tab 导航器的定义位于app/navigators/DemoNavigator.tsx在这里可以定制 Tab 栏的整体样式与行为也可以单独配置每一个 Tab。DemoNavigator.tsx 的实现展示了几个实用的配置点tabBarHideOnKeyboard: true键盘弹出时自动隐藏 Tab 栏避免遮挡输入区域tabBarStyle结合useSafeAreaInsets()的bottom值动态计算高度bottom 70适配全面屏手势条每个Tab.Screen通过options配置tabBarLabel配合translate()实现国际化与tabBarIcon按focused状态切换图标与着色外层用EpisodeProvider包裹 Tab 容器为 Tab 内的屏幕提供共享的 Context 数据。除了点击 Tab 按钮你还可以通过熟悉的导航 API 在代码中切换 Tab// 当前在 DemoShowroom 这个 Tab跳转到 DemoDebug 这个 Tab navigation.navigate(DemoDebug)Tab 既可以像 Ignite demo 中那样直接对应单个屏幕也可以挂载一个独立的栈导航器让用户在同一个 Tab 内做多级页面跳转。以消息收件箱为例const InboxStack createNativeStackNavigator() function InboxStackScreen() { return ( InboxStack.Navigator InboxStack.Screen nameList component{ListScreen} / InboxStack.Screen nameMessageDetails component{MessageDetailsScreen} / /InboxStack.Navigator ) }再把InboxStackScreen作为子组件挂进 Tab 导航器Tab.Screen nameInbox component{InboxStackScreen} /这样 Tab 初次显示的是ListScreen消息列表用户点击某条消息后应用在同 Tab 内压栈跳转到MessageDetailsScreen消息详情。Tab 承载 Stack、Stack 承载多级页面是 React Native 应用最常见的导航嵌套形态。实战模式三侧边栏Drawer导航Ignite 自带了一个侧边栏导航的示例实现即抽屉Drawer导航器。它没有使用 React Navigation 官方插件而是直接采用 React Native Gesture Handler 提供的DrawerLayout组件——这是 React Native 内置DrawerLayoutAndroid的跨平台替代方案支持 iOS / Android / Web 全平台。在 demo 中DrawerLayoutdemo 里以Drawer形式引入被用在 DemoShowroomScreen.tsx 中。关键配置包括Drawer drawerTypeback drawerPosition{isRTL ? right : left} renderDrawerContent{() ( View style{themed([$drawer, $drawerInsets])} {/* 侧边栏内容头像、菜单项、退出登录等 */} /View )} {/* 通过 DrawerIconButton 触发抽屉开关 */} DrawerIconButton onPress{toggleDrawer} / /DrawerrenderDrawerContent旧版 API 为renderNavigationView传入的视图就是渲染在屏幕侧边的抽屉内容。这里可以放置头部信息公司 Logo、已登录用户的头像与名称、菜单项、退出登录与设置功能等drawerTypeback、drawerPosition等属性用于控制抽屉的行为开关速度、遮罩位置与样式DrawerLayout还提供了跟踪抽屉过渡进度与状态的事件方便做联动动画DrawerIconButton见 DrawerIconButton.tsx是一个动画汉堡按钮它通过useDrawerProgress()订阅抽屉进度用react-native-reanimated的interpolate/interpolateColor把进度值映射为三根横条的位移、旋转与颜色变化——抽屉打开时汉堡图标平滑演变成关闭用的 × 形RTL 布局下动画方向会自动取反。导航工具函数navigationUtilities.tsnavigationUtilities.ts提供了三个在构建应用时非常实用的函数其源码位于 navigationUtilities.ts。getActiveRouteName获取当前路由名该函数接收当前导航状态navigation.getState()或navigationRef.getRootState()的返回值返回当前活跃路由的名字。它能够递归深入嵌套的导航器——只要某个路由还带有子状态route.state就继续向下查找直到找到最内层没有子路由的那个路由并返回其名称export function getActiveRouteName(state: NavigationState | PartialStateNavigationState): string { const route state.routes[state.index ?? 0] // 没有嵌套子状态说明这就是当前活跃路由 if (!route.state) return route.name as keyof AppStackParamList // 递归处理嵌套路由器 return getActiveRouteName(route.state as NavigationStateAppStackParamList) }典型用法是配合导航状态变化事件统计当前页面埋点// 嵌套两层导航器的场景AppStack - NestedStack - myScreen function MyScreen({ navigation }) { const routeName getActiveRouteName(navigation.getState()) // myScreen }useBackButtonHandlerAndroid 返回键处理这个自定义 hook 让你轻松指定哪些路由下按返回键直接退出应用。它只影响 Android对 iOS 无效果推荐在根AppNavigator中使用。AppNavigator中的实际调用是const exitRoutes Config.exitRoutes // 默认 [Welcome] useBackButtonHandler((routeName) exitRoutes.includes(routeName))其底层实现navigationUtilities.ts做了几层精细处理用Platform.OS ! android判断非 Android 平台直接使用iosExit恒返回falseiOS 上完全不注册监听通过BackHandler.addEventListener(hardwareBackPress, ...)订阅 Android 硬件返回事件回调里先确认navigationRef.isReady()再取当前路由名判断是否命中可退出路由列表命中则BackHandler.exitApp()退出应用未命中且可以返回canGoBack()则转为一次goBack()否则返回false交给系统默认处理canExit函数被保存在 ref 中避免每次渲染都重新注册监听。useNavigationPersistence导航状态持久化该 hook 负责在两次应用启动之间恢复导航状态让用户回到上次离开的页面。它在app.tsx根组件中通过NAVIGATION_PERSISTENCE_KEY NAVIGATION_STATE作为存储键接入const { initialNavigationState, onNavigationStateChange, isRestored: isNavigationStateRestored, } useNavigationPersistence(storage, NAVIGATION_PERSISTENCE_KEY)其行为受 config.base.ts 中的persistNavigation配置项控制取值范围为always | dev | prod | never默认dev仅在开发环境开启。navigationRestoredDefaultState函数根据该配置与__DEV__环境决定是否需要恢复always任何环境都持久化dev仅开发环境持久化默认值prod仅生产环境持久化never永不持久化。恢复流程restoreState有一个值得一提的细节它会先调用Linking.getInitialURL()检查应用是否由**深链deep link**启动——如果是则跳过状态恢复避免旧导航状态覆盖深链要打开的目标页面。持久化写入发生在onNavigationStateChange回调中每次路由变化都会用getActiveRouteName计算当前路由名开发环境下打印到控制台便于调试随后storage.save(persistenceKey, state)把整个导航状态写入 MMKV 存储。hook 返回的isRestored表示恢复是否完成根组件在恢复完成前不渲染任何内容返回null显示原生背景色你也可以替换为自定义的LoadingScreen。配套能力Navigator 生成器与 Web 深链Navigator 生成器Ignite CLI 提供了创建新导航器的生成器详见 Generators 文档。在项目根目录执行npx ignite-cli generate navigator OrderPizza该命令会在app/navigators文件夹下生成一个OrderPizzaNavigator.tsx模板见 NAMENavigator.tsx.ejs并自动完成路由类型与导航器引用的接入省去手动搭建脚手架的重复劳动。Web 深链配置app.tsx中通过Linking.createURL(/)生成 Web 端链接前缀并配置了屏幕路径映射如DemoShowroom→showroom/:queryIndex?/:itemIndex?再把linking配置传给AppNavigator。这样同一套导航结构在 Web 端也能获得 URL 深链与浏览器前进/后退支持。关于 Expo Router 的说明Ignite 团队正在评估用 Expo Router 驱动 Ignite 的导航体系。Expo Router 本身构建在 React Navigation 之上是一个很有前景的方案但 Ignite 的哲学是任何技术必须先经过一个或两三个完整项目的实战检验才会进入 Ignite。因此在评估完成之前ignite new时已提供切换到 Expo Router 的实验性选项本文所述的 React Navigation v7 结构仍是当前的主推方案。仓库中src/app下的_layout.tsx与index.tsx即为该实验性 Expo Router 入口可供提前体验。【免费下载链接】igniteInfinite Reds battle-tested React Native project boilerplate, along with a CLI, component/model generators, and more! 9 years of continuous development and counting.项目地址: https://gitcode.com/GitHub_Trending/ig/ignite创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考