简介
在 React 中实现路由,有两种方案,分别对应两个不同的场景
React-Router
React-Router 适用于 Web 项目。
React-Navigation
React Native 没有像浏览器那样的内置导航 API (location对象,history对象)。React Native 中的导航是通过React-Navigation来完成的。React Native 把导航和路由都集中到了 React-Navigation 中。
RN 0.44 之前,React-Navigation 在核心中维护,0.44 之后,与核心分离,独立维护。
官网:https://reactnavigation.org/ 中文手册:
https://reactnavigation.org/docs/zh-Hans/getting-started.html
注意:本节使用 React-Navigation 5.x 的版本来讲解
React-Navigation 常用的组件有四个(之后我们会依次介绍)
StackNavigator
栈导航器在 React Native 中的作用,相当于 BOM 中的 history 对象。用来跳转页面和传递参数。与浏览器端导航不同的是。StackNavigator还提供了路由之间的手势和动画。只有声明了栈导航之后,才能在 React Native 中执行跳转。
TabNavigator
标签导航器(例如:底部标签栏),用来区分模块。
DrawerNavigator
抽屉导航器,在 App 侧面划出的导航页面。
MaterialTopTabNavigator
支持左右滑动的 Tab 菜单
基础安装
在正式开始学习 React Native 路由之前,我们需要先安装相关的组件和依赖
安装组件
#安装react-navigation核心组件
yarn add @react-navigation/native@^5.x
#安装相关的依赖
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
链接组件
安装完成之后,我们还需要将相关组件和依赖,连接到操作系统平台中(Android 或 iOS)。
从 0.60 之后, 链接会自动执行. 因此,我们不再需要运行react-nativelink
但是,如果你是在 iOS 下,还需要运行下面的命令来完成链接
npx pod-install ios
添加头部组件
最后一步,你需要将如下代码,放到应用的头部(例如:放到index.js或App.js文件的头部)
import 'react-native-gesture-handler';
//其他引入
注意:如果你忽略了这一步,你的应用上线后可能会崩溃(虽然开发环境一切正常
添加导航容器
我们需要在入口文件中,把整个应用,包裹在导航容器(NavigationContainer)中(例如:在index.js或App.js文件中)。然后将其他应用代码,写在 NavigationContainer 组件中间。
import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
export default function App() {
return (
<NavigationContainer>
{/* /*具体的导航*/ */}
</NavigationContainer>
);
}
使用具体的导航
完成以上 4 个步骤后,就可以在导航容器中使用具体的导航了。下面我们会一一介绍具体的导航。主要包括:
Stack 导航
BottomTab 导航
Drawer 导航
MaterialTopTab 导航
Stack 导航
在浏览器中,我们可以通过标签,来实现不同页面之间的跳转。当用户点击链接时,URL 地址会被推送到 history 的栈中。当用户点击回退按钮时,浏览器会从 history 栈的顶部弹出一项,然后我们所处的当前页面,其实就是之前访问过的页面。但是,RN 中没有浏览器的内置 history 栈。而 ReactNavigation 的Stack 导航实现了类似浏览器端 history 栈的功能。可以在 RN 的不用屏幕之间进行跳转,并管理跳转地址。在 RN 中,如果想做跳转。
必须先声明 Stack 导航
安装组件
yarn add @react-navigation/stack
使用组件
import React, { Component } from 'react'
import { Text, StyleSheet, View, Button, TouchableOpacity } from 'react-native'
import { createStackNavigator } from '@react-navigation/stack'
function HomeScreen(prop) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>Home Screen</Text>
<Button title={"跳到新闻页面"} onPress={() => prop.navigation.navigate('News')} />
</View>
)
}
function NewsScreen(prop) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>News Screen</Text>
<Button title={"跳到 Home 页面"} onPress={() => prop.navigation.navigate('Home')} />
</View>
)
}
const Stack = createStackNavigator()
export default class index extends Component {
render() {
return (
<Stack.Navigator
initialRouteName="News"
// headerMode={'none'}
>
<Stack.Screen name="Home" component={HomeScreen}
options={{
title: "首页",
headerStyle: {
backgroundColor: 'tomato'
},
headerRight: () => (
<TouchableOpacity onPress={() => alert('Hello')}>
<Text>Hello</Text>
</TouchableOpacity>
)
}}
/>
<Stack.Screen name="News" component={NewsScreen} />
</Stack.Navigator>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
text: {
fontSize: 40
}
})
我们可以通过 Screen 组件来声明路由。
Screen 组件有两个必选属性,分别是 name 和 component。
name 是路由名称
component 是组件名称(不接受函数)
路由声明之后,我们可以通过 navigate 方法来执行屏幕间的跳转。如下图:
类组件可以通过this.props.navigation.navigate(路由名称)方式来跳转
扩展属性
BottomTab 导航
安装组件
yarn add @react-navigation/bottom-tabs
使用组件
import React, { Component } from 'react'
import { Text, StyleSheet, View, Button } from 'react-native'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import Ionicons from 'react-native-vector-icons/Ionicons'
function HomeScreen(prop) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>Home Screen</Text>
</View>
)
}
function NewsScreen(prop) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>News Screen</Text>
</View>
)
}
const Tab = createBottomTabNavigator()
export default class index extends Component {
render() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName
if (route.name === 'Home') {
iconName = focused ? 'add-circle' : 'add-circle-outline'
} else if (route.name === 'News') {
iconName = focused ? 'person' : 'person-outline'
}
return <Ionicons name={iconName} size={size} color={color} />
}
})}
tabBarOptions={{
activeTintColor: 'tomato',
inactiveTintColor: 'gray'
}}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="News" component={NewsScreen} />
</Tab.Navigator>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
text: {
fontSize: 40
}
})
为 Tab 导航菜单,设置小图标
安装图标组件
React-native-vector-icons是著名的图标组件,包含了世界各大公司的矢量图标。使用之前先安装
npm install --save react-native-vector-icons
将图标文件关联到应用
不同环境下的关联方式不同,详情查看 https://github.com/oblador/react-native-vector-icons
iOS
项目根目录下运行:react-native link react-native-vector-icons
如果不能正常运行,请参考官方文档
Android
编辑android/app/build.gradle ( 不是android/build.gradle ) 并添加如下内容:
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
然后重新运行项目。
Drawer 导航
安装组件
yarn add @react-navigation/drawer
使用组件
import React, { Component } from 'react'
import { Text, StyleSheet, View, Button } from 'react-native'
import { createDrawerNavigator } from '@react-navigation/drawer'
import Ionicons from 'react-native-vector-icons/Ionicons'
function HomeScreen(prop) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>Home Screen</Text>
<Button title={"Open Drawer"} onPress={() => prop.navigation.openDrawer()} />
<Button title={"Toggle Drawer"} onPress={() => prop.navigation.toggleDrawer()} />
</View>
)
}
function NewsScreen(prop) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>News Screen</Text>
<Button title={"Open Drawer"} onPress={() => prop.navigation.openDrawer()} />
<Button title={"跳到 Home 页面"} onPress={() => prop.navigation.navigate('Home')} />
</View>
)
}
const Drawer = createDrawerNavigator()
export default class index extends Component {
render() {
return (
<Drawer.Navigator
drawerStyle={{
width: 180,
backgroundColor: '#dfb'
}}
drawerPosition={'right'}
drawerType={'slide'}
drawerContentOptions={{
activeTintColor: 'red',
itemStyle: { // 设置菜单项的样式
marginVertical: 20
}
}}
>
<Drawer.Screen name="Home" component={HomeScreen}
options={{
title: "首页",
drawerIcon: ({ focused, color, size }) => {
let iconName
iconName = focused ? 'home' : 'home-outline'
return <Ionicons name={iconName} size={size} color={color} />
}
}}
/>
<Drawer.Screen name="News" component={NewsScreen}
options={{
title: "新闻",
drawerIcon: ({ focused, color, size }) => {
let iconName
iconName = focused ? 'person' : 'person-outline'
return <Ionicons name={iconName} size={size} color={color} />
}
}}
/>
</Drawer.Navigator>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
text: {
fontSize: 40
}
})
MaterialTopTab 导航
生成可以左右滑动的 Tab 导航
安装
yarn add @react-navigation/material-top-tabsreact-native-tab-view
使用
import React, { Component } from 'react'
import { Text, StyleSheet, View, Button } from 'react-native'
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'
import Ionicons from 'react-native-vector-icons/Ionicons'
function OrderunpayScreen() {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>待付款</Text>
</View>
)
}
function OrderPaidScreen() {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>待发货</Text>
</View>
)
}
function OrderSentScreen() {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>待收货</Text>
</View>
)
}
function OrderFinishScreen() {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>待评价</Text>
</View>
)
}
const MTab = createMaterialTopTabNavigator()
export default class index extends Component {
render() {
return (
<MTab.Navigator
tabBarPosition="bottom"
tabBarOptions={{
tabStyle: {
borderWidth: 1,
borderColor: 'red'
},
labelStyle: {
fontSize: 20
},
activeTintColor: 'red',
inactiveTintColor: '#666',
showIcon: true
}}
>
<MTab.Screen
name="OrderUnpay"
component={OrderunpayScreen}
options={{
title: '待付款',
tabBarIcon: ({ focused, color }) => {
return (
<Ionicons name="hammer-outline" size={20} color={color} />
)
}
}}
/>
<MTab.Screen
name="OrderPaid"
component={OrderPaidScreen}
options={{
title: '待发货',
tabBarIcon: ({ focused, color }) => {
return (
<Ionicons name="arrow-redo-circle-outline" size={20} color={color} />
)
}
}}
/>
<MTab.Screen
name="OrderSent"
component={OrderSentScreen}
options={{
title: '待收货',
tabBarIcon: ({ focused, color }) => {
return (
<Ionicons name="arrow-redo-outline" size={20} color={color} />
)
}
}}
/>
<MTab.Screen
name="OrderFinish"
component={OrderFinishScreen}
options={{
title: '待评价',
tabBarIcon: ({ focused, color }) => {
return (
<Ionicons name="chatbubble-ellipses-outline" size={20} color={color} />
)
}
}}
/>
</MTab.Navigator>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
text: {
fontSize: 40
}
})
属性配置
tabBarPosition
标签显示的位置,默认是 top,如果想把标签设置在底部,可以使用 bottom。
tabBarOptions
包含 tabBar 组件属性的对象。