1 前言
RN 菜鸟一枚,才开始入手,所以该文章适合新手或者基础开发者观看,希望能对你有所帮助。
在使用 React 开发 web 端页面时,会使用 React Router 进行路由管理,那么在 React Native 中,怎么进行路由管理呢?答案就是 React Navigation。 首先第一点,个人觉得它的 api 设计就非常棒。
2 安装
yarn add @react-navigation/native
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
3 必须掌握的基本概念
想要学废 React Navigation,必须要掌握以下几个点。
3.1 Screen - 官方文档
React Native 中有很多的 Screen,概念类似于 React Router 中的 Route,将路由和 Component 进行联系起来。
// React Router
<Route path="/login" component={LoginComponent} />
// React navigation
<Stack.Screen name="Login" component={LoginScreen} />
3.2 Navigators - 官方文档
Navigators 允许你定义项目的导航结构,还支持很多配置,比如headers、tab bars等. 它类似于 React Router 中里面的 Router,一个 Router可以理解为需要负责一组 Route 的相互跳转。同理,一个 Navigators 也需要负责一组 Screen 的相互跳转,在实际使用的时候,React Navigation 做了一层封装,将常用的几种布局封装成了不同的 Navigator。
3.2.1 Stack Navigator - 官方文档
Stack Navigator 基本等同于 Router + Switch,每次切换整个页面都会切换
// React Router 对等实现
<Suspense fallback={<LoadableLoading />}>
<Router history={history}>
<Switch>
<Route path="/login" component={Login} />
</Switch>
</Router>
</Suspense>
// React Navigation Stack Navigator
<RootSiblingParent>
<Provider store={store}>
<SafeAreaView style={styles.container}>
<NavigationContainer>
<Stack.Navigator initialRouteName='logged'>
<Stack.Screen name='Login' component={LoginScreen} options={{ headerShown: false }} />
<<Stack.Navigator/>
</NavigationContainer>
</SafeAreaView>
</Provider>
</RootSiblingParent>
3.2.2 Drawer Navigator - 官方文档
Drawer Navigation 类似于在 Router + Switch 的基础上额外增加了一个 Drawer 组件,这个组件放在父组件,会在访问每个子路由的时候都会展示。
// React Route
<Router>
<Route name="/" component={DrawerComponent} />
<Switch>
<Route name="/home" component={HomeComponent} />
<Route name="/ddd" component={DddComponent} />
</Switch>
</Router>
//Drawer Navigator
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
</NavigationContainer>
3.2.3 Bottoms Tab Navigator - 官方文档
Drawer Navigator 类似于在 Router + Switch 的基础上额外增加了一个底部 Tab 组件,这个组件放在父组件,会在访问每个子路由的时候都会展示
//Drawer Navigator
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="ddd" component={DddScreen} />
</Tab.Navigator>
</NavigationContainer>
3.2.4 Material Bottoms Tab Navigator - 官方文档
Drawer Navigator 类似于在 Router + Switch 的基础上额外增加了一个底部 Tab 组件,这个组件放在父组件,会在访问每个子路由的时候都会展示
//Drawer Navigator
import { createMaterialBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createMaterialBottomTabNavigator();
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="ddd" component={DddScreen} />
</Tab.Navigator>
</NavigationContainer>
3.3 跳转和页面栈
和浏览器类似,Native 也是用栈来保存访问历史记录, 但是不同与浏览器只有入栈一个页面,出栈一个页面两种操作,Native 提供了更多的对页面栈的操作接口,React Navigation 在此之上做封装成了几个 API。
3.3.1 navigation.navigate(screen) 入栈新页面
-
如果当前已经是这个 Screen,则不会跳转
-
如果页面栈内已经有这个 Screen,则将这个 Screen 上的所有 Screen 出栈
3.3.2 navigation.push(screen) 入栈新页面
注意:如果当前已经是这个 Screen,则栈内会有两个同样的 Screen
3.3.3 navigation.replace(screen) 替换当前页面(待支付到支付完成页面 后退不需要到待支付就可以用这个
3.3.4 navigation.goback(screen) 后退一页 关闭当前页
3.3.5 navigation.pop() 默认返回到上一页
3.3.6 navigation.popToTop() 返回至最顶层页面
- 去到第一个页面 - 即只保留页面栈最底层页面,将其余所有页面出栈
4 页面生命周期和 useHooks(很重要)
我只会列举一下常用的 hooks,感兴趣的可以去官网查看更多。 在web 环境中,我们一般就关注三个生命周期,
- mount:页面创建
- update:页面更新
- destroy:页面销毁或者注销 对于 Native 环境来说,除了上面的之外,需要额外关心的两个页面生命周期
- App 切到后台
- App 切回前台
4.1 addListener - 官方文档
const unsubscribe = navigation.addListener('tabPress', (e) => {
// Prevent default action
e.preventDefault();
});
function Profile({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// do something
});
return unsubscribe;
}, [navigation]);
return <ProfileContent />;
}
-
focus - 当屏幕成为焦点时,会发出这个事件
-
blur - 当屏幕失焦时会发出这个事件
-
beforeRemove -当用户离开屏幕时, 防止用户离开
-
state (advanced) - (高级)-当导航器的状态改变时发出此事件
-
tabPress (advanced) - 监听 tab 页切换
4.2 useFocusEffect - 官方文档
对此 React Navigation 封装了 useFocusEffect
,采用了类似 useEffect
的设计
import { useFocusEffect } from '@react-navigation/native';
function Profile({ userId }) {
const [user, setUser] = React.useState(null);
useFocusEffect(
React.useCallback(() => {
const unsubscribe = API.subscribe(userId, user => setUser(user));
return () => unsubscribe();
}, [userId])
);
return <ProfileContent user={user} />;
}
4.3 useIsFocused - 官方文档
判断当前页面的焦点状态
注意: 这个 hook 会触发页面 re-renders,如果你的页面组件很多,建议使用 React.useMemo 或者 React.PureCompnent 来减少 re-renders。
import { useIsFocused } from '@react-navigation/native';
function Profile() {
const isFocused = useIsFocused();
return <Text>{isFocused ? 'focused' : 'unfocused'}</Text>;
}
4.4 useNavigation - 官方文档
这个hook 类似于 React Router 中的 useHistory 函数,useNavigation 可以访问 navigation 对象。当你不想使用一直传递 navigation 时,就可以使用这个方法。
import * as React from 'react';
import { Button } from 'react-native';
import { useNavigation } from '@react-navigation/native';
function MyBackButton() {
const navigation = useNavigation();
return (
<Button
title="Back"
onPress={() => {
navigation.goBack();
}}
/>
);
}
4.5 useRoute - 官方文档
它可以用来获取 route 参数中的 object,类似于 React Router 中的 useLocation,
import * as React from 'react';
import { Text } from 'react-native';
import { useRoute } from '@react-navigation/native';
function MyText() {
const route = useRoute();
return <Text>{route.params.caption}</Text>;
}
常用页面层级架构
我们一般 app 常用的页面一般是 Login、LoggedScreen、Setting 简单层级图如下:
我们再来结合一下 Sreen 和 Navgation 画一下
最后
React Router 和 React Navigation 都是一个了不起的库,它可以在一个页面模拟出中多页面的情况,并具有很高的可用性。