每天对自己多问几个为什么,总是有着想象不到的收获。 一个菜鸟小白的成长之路(copyer)
react-native官网的主推方案就是一个单独的导航库react-navigation
官网地址
https://reactnavigation.org/
react-navigation有很多的版本,并且版本的变动还是很大的,对我我刚接触react-native还是从最新的版本开始学习, 最近的版本就是 6.x版本
安装
yarn add @react-navigation/native // 安装导航库
yarn add react-native-screens react-native-safe-area-context // 安装导航的依赖库
yarn add @react-navigation/native-stack // 安装栈路由
yarn add @react-navigation/bottom-tabs // 安装tab路由
yarn add @react-navigation/drawer // 安装抽屉路由
使用
这里就结合官网的,边看边写。
1、根组件
要想在项目中使用路由,需要
@react-navigation/native
提供的一个组件NavigationContainer
在 项目的根index.js
或者app.js
中作为根组件
错误总结:
这里 NavigationContainer
要作为根组件,不然会没有现象
// 错误的写法, 这里路由就没有生效
<View>
<Text>测试项目11</Text>
<NavigationContainer>
<Navigator initialRouteName="Home">
<Screen name="Home" component={
Home} />
<Screen name="About" component={
About} />
</Navigator>
</NavigationContainer>
</View>
// 正确的写法
<NavigationContainer>
<Text>测试项目11</Text>
<Navigator initialRouteName="Home">
<Screen name="Home" component={
Home} />
<Screen name="About" component={
About} />
</Navigator>
</NavigationContainer>
2、编写栈路由
这里使用
栈路由
, 就需要使用@react-navigation/native-stack
导出的一个函数createNativeStackNavigator
createNativeStackNavigator
是一个函数,返回一个对象,包含两个属性Screen
和Navigator
。都是用来配置react-navigation路由导航。Navigator
组件应该包含Screen
组件,用来定义路由。
import React from 'react'
import {
View, Text, Button } from 'react-native'
import {
NavigationContainer } from '@react-navigation/native'
import {
createNativeStackNavigator } from '@react-navigation/native-stack'
const Home = () => {
return (
<View style={
{
flex: 1, backgroundColor: '#84bf96', justifyContent: 'center', alignItems: 'center'}}>
<Text>Home</Text>
</View>
)
}
//调用 createNativeStackNavigator 方法
const Stack = createNativeStackNavigator()
// 结构返回出来的对象
const {
Navigator, Screen} = Stack
const App = () => {
return (
<NavigationContainer>
// 包含关系
<Navigator>
// 路由配置,取名,以及渲染的组件
<Screen name="Home" component={
Home} />
</Navigator>
</NavigationContainer>
)
}
export default App;
效果图:
3、路由跳转
使用
Screen
组件,这个组件是一个高阶组件,会增强props
,在使用的组件中,会携带一个navigation
对象在这个对象中,又包含一个
navigate
属性,是一个函数,是用来跳转 页面的,传递的参数 ,就是 在 使用Screen
中的name
值的属性(简单来说,就是跟名字跳转页面)黄色的区域: 就是说,如果传递了一个没有定义的name,那就整个程序就会报错。换一种方法来说,就是navigate的接受值,是我们已经定义好了的
<NavigationContainer>
<Navigator initialRouteName="Home">
<Screen name="Home" component={
Home} />
<Screen name="About" component={
About} />
</Navigator>
</NavigationContainer>
这里注册了 Home
和 About
两个name值,就是给 navigate
使用的
const Home = (props) => {
// 在props中拿到 navigation 对象
const {
navigation } = props
return (
<View style={
{
flex: 1}}>
<Text>Home</Text>
// 使用navigate函数,根绝name值,来就行跳转页面
<Button onPress={
() => navigation.navigate('About')} title="跳转"/>
</View>
)
}
其他的方式跳转路由
第一段,简单来说,就是navigate接受
相同的参数,是不会跳转的
。
navigate('about')
第一次执行,会进行调转 about页面中
navigate('about')
第二次执行,如果当前已经在about页面中,navigate函数就不会进行任何的处理。第二段:就是我们就是想不停的近同一个页面(渲染的时候,根据传递过来的参数,渲染不同的效果,但是都是同一个组件)
那么这里就推荐
push
的方式,使用push方法,每次都会添加一个新的导航到导航栈中
<Button
title="Go About"
onPress={
() => navigation.push('About')}
/>
返回的跳转方式
goBack()
:返回上一级
popToTop
: 返回到栈顶(最开始渲染的页面)
<Button
title="返回"
onPress={
() => navigation.goBack()}
/>
<Button
title="返回首页"
onPress={
() => navigation.popToTop()}
/>
4、路由传参
路由传参,
navigate
接受第二个参数,对象的形象,传递参数组件接受路由传递的参数,
route
也是Screen
组件 增强的 props,route也是一个对象
,里面包含 一个 属性params
, 就是用来接受传递过来的参数
// Home组件: 传递参数
const Home = (props) => {
const {
navigation } = props
return (
<View style={
{
flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Home</Text>
// 传递参数
<Button onPress={
() => navigation.navigate('About', {
name: 'copyer'})} title="跳转"/>
</View>
)
}
// About组件:接受参数
const About = (props) => {
const {
params } = props.route
return (
<View style={
{
flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>About</Text>
<Text>接受的路由参数:{
params.name}</Text>
</View>
)
}
效果图:
其他要点
组件可以跟新
params
,像跟新内部 的state一样,使用setParams()
const About = ({
route, navigation}) => {
const {
params } = route
return (
<View style={
{
flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>About</Text>
<Text>接受的路由参数:{
params.name}</Text>
// 跟新 params
<Button title="跟新" onPress={
() => navigation.setParams({
name: 'james'
})}></Button>
</View>
)
}
效果图
初始化
params
,如果没有指定特别的params
就会实现初始化值,并且初始化的对象,也会进行浅层的合并,使用的属性initialParams
// 初始化的默认值
<Screen name="About" component={
About} initialParams={
{
name: 'kobe'}} />
// 传递参数
navigation.navigate('About', {
name: 'james'})
这里传递的值 》》 高于初始化的值 (展示传递的值)
// 初始化的默认值
<Screen name="About" component={
About} initialParams={
{
name: 'kobe'}} />
// 传递参数
navigation.navigate('About', {
age: '12'})
这里就会进行浅层的合并
route.params = {
name: 'kobe',
age: '12'
}
这里主要说的是: 路由传递参数的时候,应该是最小化的数据,而不会传递完整的数据,这样会造成意外的错误。(数据不同步,有些地方修改了,在路由中是体现不出来的),每个页面的数据,最好通过唯一性的值,去获取详细的数据。
5、配置路由头部
Screen
组件 接受options
作为 props,
options
可以是一个对象,还可以是一个函数,返回一个对象
<Stack.Screen
name="About"
component={
About}
options={
({
route }) => ({
title: route.params.name })}
/>
这里 options是一个函数的时候,接受的参数,跟props里面增强的 是一样的,都有 navigation
和 route
更新
options
,使用setOptions
设置顶部的样式的一些配置属性
headerStyle
: 对顶部盒子的样式设置
headerTintColor
:返回按钮
和title
都是使用这个颜色
haederTitleStyle
: 单独对title
的样式设置
<Screen name="Home"
component={
Home}
options={
{
headerStyle:{
backgroundColor: 'yellow'},
headerTintColor: 'red',
headerTitleAlign: 'center',
headerTitleStyle: {
fontWeight: 'bold'
}
}} />
针对多个路由都使用相同的样式,就可以写在
navigator
组件上
<Navigator
screenOptions={
{
headerStyle:{
backgroundColor: 'yellow'},
headerTintColor: 'red',
headerTitleAlign: 'center',
headerTitleStyle: {
fontWeight: 'bold'
}
}}
>
<Screen
name="Home"
component={
Home}
options={
{
title: 'My home' }}
/>
<Screen
name="About"
component={
About}
options={
{
title: 'About' }}
/>
</Navigator>
其他的配置:
headerTitle
: 设置title是一个渲染的组件
headerRight
: 设置顶部右边的组件
headerLeft
: 设置顶部左边的组件
6、嵌套路由
function Home() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={
Feed} />
<Tab.Screen name="Messages" component={
Messages} />
</Tab.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={
Home}
options={
{
headerShown: false }}
/>
<Stack.Screen name="Profile" component={
Profile} />
<Stack.Screen name="Settings" component={
Settings} />
</Stack.Navigator>
</NavigationContainer>
);
}
嵌套路由的结构
7、路由的生命周期
使用
navigate
方法跳转,A
跳转到B
,那么 组件的componentDidMount
会执行,当使用goBack()
来返回
时,componentWillUnmount
也会执行。当然web端的生命周期来用于
react-navigatioin
, 肯定有着不同之处。
考虑一种情况,当进入
A页面
,componentDidMount
被执行,当A->B
时(使用push()
方法),B
的componentDidMount
被执行,但是A
页面是一致处于挂载中,所以componentWillUnmount
是不会执行的。当
B返回到A
时, B的componentWillUnmount
是会被执行的,但是A的componentDidMount
是不会执行的,因为A的页面是一直处于挂载的状态。
function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="First">
{
() => (
<SettingsStack.Navigator>
<SettingsStack.Screen
name="Settings"
component={
SettingsScreen}
/>
<SettingsStack.Screen name="Profile" component={
ProfileScreen} />
</SettingsStack.Navigator>
)}
</Tab.Screen>
<Tab.Screen name="Second">
{
() => (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={
HomeScreen} />
<HomeStack.Screen name="Details" component={
DetailsScreen} />
</HomeStack.Navigator>
)}
</Tab.Screen>
</Tab.Navigator>
</NavigationContainer>
);
}
四个路由,被加载后,都会保存状态。
现在的目标就是: 想知道哪个组件处于活跃状态,然后单独做一些事情
1、监听 focus
事件
function Profile({
navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// Screen was focused
// Do something
});
return unsubscribe;
}, [navigation]);
return <ProfileContent />;
}
2、使用钩子函数
import {
useFocusEffect } from '@react-navigation/native';
function Profile() {
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
return () => {
// Do something when the screen is unfocused
// Useful for cleanup functions
};
}, [])
);
return <ProfileContent />;
}
Tab Navigation的实例
import * as React from 'react';
import {
Button, Text, View } from 'react-native';
import {
NavigationContainer } from '@react-navigation/native';
import {
createNativeStackNavigator } from '@react-navigation/native-stack';
import {
createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function DetailsScreen() {
return (
<View style={
{
flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function HomeScreen({
navigation }) {
return (
<View style={
{
flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen</Text>
<Button
title="Go to Details"
onPress={
() => navigation.navigate('Details')}
/>
</View>
);
}
function SettingsScreen({
navigation }) {
return (
<View style={
{
flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Details"
onPress={
() => navigation.navigate('Details')}
/>
</View>
);
}
const HomeStack = createNativeStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={
HomeScreen} />
<HomeStack.Screen name="Details" component={
DetailsScreen} />
</HomeStack.Navigator>
);
}
const SettingsStack = createNativeStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={
SettingsScreen} />
<SettingsStack.Screen name="Details" component={
DetailsScreen} />
</SettingsStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={
HomeStackScreen} />
<Tab.Screen name="Settings" component={
SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
设置图标
<Tab.Navigator
screenOptions={
({
route }) => ({
tabBarIcon: ({
focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-information-circle'
: 'ios-information-circle-outline';
} else if (route.name === 'Settings') {
iconName = focused ? 'ios-list-box' : 'ios-list';
}
// You can return any component that you like here!
return <Ionicons name={
iconName} size={
size} color={
color} />;
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
})}
>
</Tab.Navigation>
Drawer Navigation的实例
import * as React from 'react';
import {
Button, View } from 'react-native';
import {
createDrawerNavigator } from '@react-navigation/drawer';
import {
NavigationContainer } from '@react-navigation/native';
function HomeScreen({
navigation }) {
return (
<View style={
{
flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
onPress={
() => navigation.navigate('Notifications')}
title="Go to notifications"
/>
</View>
);
}
function NotificationsScreen({
navigation }) {
return (
<View style={
{
flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={
() => navigation.goBack()} title="Go back home" />
</View>
);
}
const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={
HomeScreen} />
<Drawer.Screen name="Notifications" component={
NotificationsScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
总结
对花了一下午的时间,对react-navigation
有着大体的认识,在开发过程中,感觉还是可以理解代码了。知识学不完,只有自己慢慢充实。