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)