4. 路由
expo
我mac 用的是expo来做环境变量的,expo有自己的router
expo路由是一个约定式的路径,放在app目录下的文件及文件夹都是路由
_layout.jsx 它定义共享的 UI 元素,例如标题和标签栏,以便它们在不同的路由之间保持一致
屏幕之间切换
import { Link } from 'expo-router';
....
<Link href="/Page1">page1 </Link>
设置标题栏
注意:android是不支持返回标题的设置的,这些长度内容都有限制
import React from 'react';
import { View, Text, Button } from 'react-native'
import { Stack, useLocalSearchParams, router } from 'expo-router';
import { TextInput } from 'react-native';
export default function Page1() {
  const params = useLocalSearchParams<{ [key: string]: string}>();
  return <View style={{ flex: 1, backgroundColor: '#eee', paddingTop: 30 }}>
    <Stack.Screen
      options={{
        title: 'My home',
        headerStyle: { backgroundColor: 'green' },
        headerTintColor: '#fff',
        headerTitleStyle: {
          fontWeight: 'bold',
        },
        headerBackTitle: '返回',
        headerRight() {
          return <Button 
            title={params?.mode === 'edit' ? '保存' : '编辑'}  
            onPress={() => {
              router.setParams({ mode: params?.mode === 'edit' ? 'save' : 'edit'})
            }}
          />
        }
      }}
    />
    <Text>欢迎来到Page1</Text>
    <TextInput 
      editable={params?.mode === 'edit'}
      style={{
        borderColor: '#ccc',
        borderWidth: 1,
        height: 50,
      }}
    />
  </View>
}
        
隐藏标题栏
import { useNavigation } from 'expo-router';
...
const navigation = useNavigation();
useEffect(() => {
    navigation.setOptions({ headerShown: false });
}, [navigation]);底部导航
在expo中,要创建底部导航,在app/(tabs)目录下创建,然后在_layout.jsx中导入就可以
import { Tabs } from 'expo-router';
import React from 'react';
import { TabBarIcon } from '@/components/navigation/TabBarIcon';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export default function TabLayout() {
  const colorScheme = useColorScheme();
  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
        // headerShown: false, // 隐藏顶部导航
      }}>
      <Tabs.Screen
        name="index"
        options={{
          title: 'Home',
          tabBarIcon: ({ color, focused }) => (
            <TabBarIcon name={focused ? 'home' : 'home-outline'} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="explore"
        options={{
          title: 'Explore',
          tabBarIcon: ({ color, focused }) => (
            <TabBarIcon name={focused ? 'code-slash' : 'code-slash-outline'} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="Page1"
        options={{
          tabBarActiveTintColor: '#f00',
          tabBarLabel: (params) => {
            return <Text style={{ color: params.color}} >Page1</Text>
          },
          tabBarIcon: ({ color, focused }) => (
            <TabBarIcon name={focused ? 'code-slash' : 'code-slash-outline'} color={color} />
          ),
        }}
      />
    </Tabs>
  );
}
    

tabBarLabel 可以用来设置字体颜色等
顶部导航
在标题栏下方还可以加一个导航,官方文档
        还有一个库更加灵活:react-native-pager-view, 这个库比上面的库增加了动画
        
安装
npm install @react-navigation/material-top-tabs react-native-tab-view
npm install react-native-pager-view
# Mac iOS
npx pod-install ios
使用
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
const Tab = createMaterialTopTabNavigator();
function MyTabs() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}    替换屏幕
就是跳转到新屏幕内容后,无法返回
import { router } from 'expo-router';
...
<Text onPress={() => router.replace('/Page1')}>page1 </Text>
...
抽屉导航器
安装依赖
        npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
在app/_layout.jsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { Drawer } from 'expo-router/drawer';
  return (
    <GestureHandlerRootView style={{ flex: 3 }}>
    {/* <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="+not-found" />
      </Stack>
    </ThemeProvider> */}
        <Drawer>
          <Drawer.Screen
            name="Page1" // This is the name of the page and must match the url from root
            options={{
              drawerLabel: 'Page1',
              title: 'overview',
            }}
          />
        </Drawer>
      </GestureHandlerRootView>
  );    
传统的react native 屏幕跳转
这里都是抄其它地方来的,自行验证
    # 安装路由库
    npm i --save react-navigation然后创建方式去官网看一下吧,每个版本都不太一样的
在react-navigation中有常用的导航器有以下7种:
- createStackNavigator:类似于普通的Navigator,屏幕上方导航栏;
 create TabNavigator: create TabNavigator已弃用,使用createBottomTabNavigator和/或createMaterialTopTabNavigator替代;- createBottom TabNavigator:相当于iOS里面的TabBarController,屏幕下方的标签栏;
 - createMaterialTopTabNavigator:屏幕顶部的材料设计主题标签栏;
 - createDrawerNavigator:抽屉效果,侧边滑出;
 - createSwitchNavigator:SwitchNavigator 的用途是一次只显示一个页面。
 
你可以通过以上7种导航器来创建你APP,可以是其中一个也可以多个组合,这个可以根据具体的应用场景并结合每一个导航器的特性进行选择。
• Screen navigation prop(屏幕导航属性):通过navigation可以完成屏幕之间的调度操作,例如打开另一个屏幕;
• screen navigationdptions(屏幕导航选项):通过navigationOptions可以定制导航器显示屏幕的方式(例如:头部标题,选项卡标签等);
导航器所支持的Props
const SomeNav = createStackNavigator/createBottomTabNavigator/createMaterialTopTabNaviga
// config
}) ;
const AppNav = createAppContainer (SomeNav) ;
<AppNav
    screenProps={xxx}
    ref={nav = { navigation = nav; }}
    onNavigationStateChange=(prevState, newState, action)=>{
    }
/>- ref:可以通过 ref 属性获取到 navigation;
 - onNavigationStateChange(prevState, newState, action):顶级节点除了 ref 属性之外,还接受 onNavigationStateChange(prevstate,newState,action) 属性,每次当导航器所管理的 state 发生改变时,都会回调该方法;
 - prevState:变化之前的state;
 - newState: 新的state;
 - 导致state变化的action;
 - screenProps:向子屏幕传递额外的数据,子屏幕可以通过this.props.screenProps获取到该数据。
 
Screen Navigation Prop(屏幕的navigation Prop)
当导航器中的屏幕被打开时,它会收到一个 navigation prop,navigation prop是整个导航环节的关键一员,接下来就详细讲解一下 navigation 的作用。
this.props.navigation包含一下功能:
- navigate:跳转到其他界面;
 - state:屏幕的当前state;
 - setParams: 改变路由的params;
 - goBack:关闭当前屏幕;
 - dispatch:向路由发送一个action;
 - addListener:订阅导航生命周期的更新;
 - isFocused:true 标识屏幕获取了焦点;
 - getParam:获取具有回退的特定参数;
 - dangerouslyGetParent: 返回父导航器;
 
StackNavigator的navigation的额外功能:
当且仅当当前 navigator 是 stack navigator 时,this.props.navigation 上有一些附加功能。这些函数是 navigate 和 goBack 的替代方法,你可以使用任何你喜欢的方法。这些功能是:
- this.props.navigation
 - push- 导航到堆栈中的一个新的路由
 - pop - 返回堆栈中的上一个页面
 - popToTop- 跳转到堆栈中最顶层的页面
 - replaite-用新路由替换当前路由
 - reset- 擦除导航器状态并将其替换为多个操作的结果
 - dismiss - 关闭当前栈
 
使用navigate进行界面之间的跳转
navigation.navigate ({routeName, params, action, key}) 或 navigation navigate(routeName, params, action)
routeName:要跳转到的界面的路由名,也就是在导航其中配置的路由名;
params:要传递给下一个界面的参数;
action:如果该界面是一个navigator的话,将运行这个sub-action;
key:要导航到的路由的可选标识符。如果已存在,将后退到此路由;
export const AppStackNavigator = createStackNavigator ({
    HomeScreen: {
        screen: HomeScreen
    },
    Pagel: {
        screen: Page1
    })
class HomeScreen extends React. Component {
    render () {
        const {navigate} = this.props.navigation;
        
        return (
            <View>
                ‹Text>This is HomeScreen</Text>
                ‹Button
                    onPress=(() => navigate( 'Pagel', {name: 'Devio'7)}
                    title="Go to Pagel"
                />
            </View>
        )
    }
}
使用state的params
可以通过this.props.state.params来获取通过 setParams(),或 navigation.navigate()传递的参数。
‹Button
    title={params.mode === ‘edit,?‘保存’:'编辑'}
    onPress=1( =>
        setParams ({mode: params-mode === 'edit' ? " : 'edit'})}
/>
<Button
    title="Go To Pagel"
    onPress={() => {
        navigation.navigate( 'Pagel', { name: 'Devio' });
    }}
const {navigation} = this.props;
const {state, setParams} = navigation;
const {params} = state;
const showText = params.mode === 'edit'?'正在编辑’:'编辑完成’;
使用setParams 改变route params
setParams: function setParams(params):我们可以借助 setParams 来改变route params,比如,通过 setParams 来更新页面顶部的标题,返回按钮等;
class ProfileScreen extends React. Component 1
    render) {
        const {setParams} = this.props.navigation;
        return (
            <Button
                onPress={() => setParams ({name: 'Ming'})}
                title="Set title name to 'Jack'"
            />
        )
    }
}
使用goBack返回到上一页面或指定页面
goBack:function goBack(key):我们可以借助goBack返回到上一页或者路由栈的指定页面。
其中 key 表示你要返回到页面的页面标识如 id-1517035332238-4,不是routeName。
可以通过指定页面的 navigation.state.key 来获得页面的标识。
key非必传,也可传null。
navigation.state {params: {...}, key: "id-1517035332238-4", routeName:"Pagel" }
通过dispatch发送一个action
dispatch:function dispatch(action):给当前界面设置action,会替换原来的跳转,回退等事件。
const resetAction = StackActions. reset({
    index: 0,
    actions: [
        NavigationActions.navigate({
            routeName: 'HomePage'
            params: {
                theme: theme,
                selectedTab: selectedTab
            },
        })
    ]
})
navigation.dispatch (resetAction)