项目结构目录如下


RnDApp/ ├── android/ # Android 原生工程(Expo Prebuild 后生成) ├── api/ # 后端接口封装层 ├── app/ # Expo Router 路由目录 │ ├── (tabs)/ # 底部 Tab 路由组 │ │ ├── discover/ # /discover │ │ ├── home/ # /home │ │ ├── my/ # /my │ │ ├── swap/ # /swap │ │ └── trade/ # /trade │ │ └── layout.tsx # Tab 布局 │ ├── profile/ # 独立路由组 │ │ ├── layout.tsx │ │ └── +not-found.tsx │ ├── createAccount.tsx # /createAccount │ ├── createWallet.tsx # /createWallet │ ├── index.tsx # 首页 / │ ├── login.tsx # /login │ └── register.tsx # /register ├── assets/ # 图片、字体、音视频等静态资源 ├── components/ # 公共业务组件 ├── constants/ # 枚举、常量、主题配置 ├── hooks/ # 自定义 React Hooks ├── node_modules/ # 依赖包 ├── scripts/ # 构建、自动化脚本 ├── stores/ # 全局状态管理(Zustand / Redux) ├── .gitignore ├── app.json # Expo 项目配置 ├── babel.config.js ├── global.css # Babel 配置(NativeWind 等) ├── metro.config.js ├── package.json ├── tailwind.config.js └── tsconfig.json

项目说明
  • 样式:Nativewind(Tailwind CSS 语法)
  • 状态:TanStack Query(服务端)+ Zustand(客户端)
  • 请求:Axios
  • 路由:Expo Router

expo-router(文件路由)

说明:文件即路由,括号文件夹不生成路径,_layout.tsx 负责导航配置
分类描述
1. 文件配置[/app/_layout.tsx]

import { useColorScheme } from '@/hooks/useColorScheme'; import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useFonts } from 'expo-font'; import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import 'react-native-reanimated'; import "../global.css"; const queryClient = new QueryClient(); export default function RootLayout() { const colorScheme = useColorScheme(); const [loaded] = useFonts({ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), }); if (!loaded) { // Async font loading only occurs in development. return null; } return ( <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}> <QueryClientProvider client={queryClient}> <Stack screenOptions={{ headerShown: false }}> <Stack.Screen name="index" options={{ headerShown: false }} /> <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> <Stack.Screen name="+not-found" /> </Stack> <StatusBar style="auto" /> </QueryClientProvider> </ThemeProvider> ); }

说明:配置了tanstack/react-query和tailwind以及路由配置:包含底部导航和入口文件以及未匹配路由页面

2. 底部导航配置[/app/tabs/_layout.tsx] & [/app/tabs/home/_layout.tsx]
  • [/app/tabs/_layout.tsx]

import { HapticTab } from '@/components/HapticTab'; import { IconSymbol } from '@/components/ui/IconSymbol'; import TabBarBackground from '@/components/ui/TabBarBackground'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import FontAwesome6 from '@expo/vector-icons/FontAwesome6'; import Ionicons from '@expo/vector-icons/Ionicons'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import { CommonActions } from '@react-navigation/native'; import { Tabs } from 'expo-router'; import React from 'react'; import { Platform } from 'react-native'; export default function TabLayout() { const colorScheme = useColorScheme(); return ( <> <Tabs screenOptions={{ tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint, headerShown: false, tabBarButton: HapticTab, tabBarBackground: TabBarBackground, tabBarStyle: Platform.select({ ios: { // Use a transparent background on iOS to show the blur effect position: 'absolute', }, default: {}, }), }}> <Tabs.Screen name="home" options={{ title: 'Home', tabBarLabel: () => null, tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />, }} listeners={({ navigation }) => ({ tabPress: (e) => { e.preventDefault(); // 阻止默认跳转 navigation.dispatch( CommonActions.reset({ index: 0, routes: [ { name: 'home', // 对应 my/index state: { routes: [{ name: 'index' }], index: 0, }, }, ], }) ); }, })} /> <Tabs.Screen name="trade" options={{ // title: 'Trade', tabBarLabel: () => null, tabBarIcon: ({ color, focused }) => ( <FontAwesome6 name="btc" size={24} color={color} /> ), }} listeners={({ navigation }) => ({ tabPress: (e) => { e.preventDefault(); // 阻止默认跳转 navigation.dispatch( CommonActions.reset({ index: 0, routes: [ { name: 'trade', // 对应 my/index state: { routes: [{ name: 'index' }], index: 0, }, }, ], }) ); }, })} /> <Tabs.Screen name="swap" options={{ // title: 'Trade', tabBarLabel: () => null, tabBarIcon: ({ color, focused }) => ( <MaterialIcons name="swap-horizontal-circle" size={24} color={color} /> ), }} listeners={({ navigation }) => ({ tabPress: (e) => { e.preventDefault(); // 阻止默认跳转 navigation.dispatch( CommonActions.reset({ index: 0, routes: [ { name: 'swap', // 对应 my/index state: { routes: [{ name: 'index' }], index: 0, }, }, ], }) ); }, })} /> <Tabs.Screen name="discover" options={{ tabBarLabel: () => null, // title: 'My', tabBarIcon: ({ color, focused }) => ( <Ionicons name="compass" size={24} color={color} /> ), }} listeners={({ navigation }) => ({ tabPress: (e) => { e.preventDefault(); // 阻止默认跳转 navigation.dispatch( CommonActions.reset({ index: 0, routes: [ { name: 'discover', // 对应 my/index state: { routes: [{ name: 'index' }], index: 0, }, }, ], }) ); }, })} /> <Tabs.Screen name="my" options={{ tabBarLabel: () => null, // title: 'My', tabBarIcon: ({ color, focused }) => ( <FontAwesome6 name="user-large" size={24} color={color} /> ), }} listeners={({ navigation }) => ({ tabPress: (e) => { e.preventDefault(); // 阻止默认跳转 navigation.dispatch( CommonActions.reset({ index: 0, routes: [ { name: 'my', // 对应 my/index state: { routes: [{ name: 'index' }], index: 0, }, }, ], }) ); }, })} /> </Tabs> </>); }

说明:listeners监听事件解决底部导航跳转默认页面(index)options主要配置导航的设置包含icon和文字

  • [/app/tabs/home/_layout.tsx]

// app/(tabs)/home/_layout.tsx import { Stack } from 'expo-router'; export default function DiscoverStack() { return ( <Stack screenOptions={{ headerShown: false }}> <Stack.Screen name="index" /> </Stack> ); }

说明:底部导航要配合使用,主要解决双导航问题

3. 文件夹不含导航配置[/app/xxx/_layout.tsx]

同上[/app/tabs/home/_layout.tsx]

汇总速查
分类 路径示例 对应路由 导航行为
独立页面 /app/index.tsx / 无父级导航,直接渲染
独立页面 /app/login.tsx /login 同上
底部导航 /app/(tabs)/home.tsx /home 自动嵌套 Tab;由 /app/(tabs)/_layout.tsx 统一配置
底部导航 /app/(tabs)/swap.tsx /swap 同上
分组文件夹(无导航) /app/profile/settings.tsx /profile/settings 仅做路径分组,不额外生成导航层级

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐