前言
在RN项目开发过程中,经常会看到如下形式的路由跳转。
render() {
return (
<View>
<Text>2</Text>
<Button
title = "跳转到指定的页面"
onPress = {() => this.props.navigation.push('ChangePassword')}
/>
<Button
title = "跳转到指定的页面"
onPress = {() => this.props.navigation.navigate('Home')}
/>
<Button
title = "返回上一个页面"
onPress = {() => this.props.navigation.goBack()}
/>
<Button
title = "返回第一个页面"
onPress = {() => this.props.navigation.popToTop()}
/>
</View>
)
}
在RN多页面应用程序开发过程中,页面跳转是通过路由或导航器来实现的。在0.44版本之前,开发者可以直接使用官方提供的Navigator组件来实现页面跳转,不过从0.44版本开始,Navigator被官方从react native的核心组件库中剥离出来,放到react-native-deprecated-custom-components模块中。
如果开发者需要继续使用Navigator,则需要先执行yarn add react-native-deprecated-custom-components命令后再使用。不过,官方并不建议开发者这么做,而是建议开发者直接使用导航库react-navigation。react-navigation是React Native社区非常著名的页面导航库,可以用来实现各种页面的跳转操作。
今天就结合RN官方推荐的路由导航组件react-navigation,深入了解下相关技能知识。
总览
React Native 中,官方推荐使用 react-navigation 来实现各个界面的跳转和不同板块的切换。react-navigation据称有原生般的性能体验效果。可能会成为未来React Native导航组件的主流军。
目前,react-navigation支持三种类型的导航器,分别是StackNavigator、TabNavigator和DrawerNavigator。具体区别如下:
1、
StackNavigator: 类似于普通的Navigator,屏幕上方导航栏,用于实现各个页面之间的跳转;
2、TabNavigator: 相当于iOS里面的TabBarController,屏幕下方的标签栏,主要用于同一个页面上不同界面之间的切换;
3、DrawerNavigator: 抽屉效果,侧边滑出;
StackNavigator 导航栏
StackNavigator(RouteConfigs, StackNavigatorConfig)
StackNavigator 组件采用堆栈式的页面导航来实现各个界面跳转。如果想要使用StackNavigator,一定要先注册导航。
其中,createStackNavigator用于配置栈管理的页面。在createStackNavigator模式下,为了方便对页面进行统一管理,首先新建一个RouterConfig.js文件,并使用createStackNavigator注册页面。对于应用的初始页面还需要使用initialRouteName进行申明。同时,导航器栈还需要使用createAppContainer函数进行包裹。示例代码如下:
RouterConfig.js文件
import {createAppContainer,createStackNavigator} from 'react-navigation';
import MainPage from './MainPage'
import DetailPage from "./DetailPage";
const AppNavigator = createStackNavigator({
MainPage: MainPage,
DetailPage:DetailPage
},{
initialRouteName: "MainPage",
},
);
export default createAppContainer(AppNavigator);
接下来,在入口文件中以组件的方式引入StackNavigatorPage.js文件即可。例如:
import StackNavigatorPage from './src/StackNavigatorPage'
export default class App extends Component<Props> {
render() {
return (
<StackNavigatorPage/>
);
}
}
然后,就可以进行导航配置了。
const Navigator = StackNavigator(
{
Tab: { screen: Tab },
Web: { screen: WebScene },
GroupPurchase: { screen: GroupPurchaseScene },
},
{
navigationOptions: {
// headerStyle: { backgroundColor: color.theme }
headerBackTitle: null,
headerTintColor: '#333333',
showIcon: true,
},
}
);
这里给导航器配置了三个界面,如果需要更多界面,可以在之后进行添加。主要有一个用于主界面之间能够切换的Tab界面和其他两个能够展示数据的界面。
StackNavigatorConfig 导航器配置
initialRouteName- 导航器组件中初始显示页面的路由名称,如果不设置,则默认第一个路由页面为初始显示页面;initialRouteParams- 给初始路由的参数,在初始显示的页面中可以通过this.props.navigation.state.params来获取;navigationOptions-路由页面的配置选项,它会被RouteConfigs参数中navigationOptions的对应属性覆盖;paths-路由中设置的路径覆盖映射配置;mode- 页面跳转方式,有card和modal两种,默认为card:
card- 原生系统默认的的跳转,左右互换;modal- 只针对iOS平台,模态跳转,上下切换;headerMode- 页面跳转时,头部的动画模式,有float 、 screen 、 none三种:
float-渐变,类似iOS的原生效果,无透明;screen- 标题与屏幕一起淡入淡出,如微信一样;none- 没有动画;cardStyle-为各个页面设置统一的样式,比如背景色,字体大小等;transitionConfig- 配置页面跳转的动画,覆盖默认的动画效果;onTransitionStart- 页面跳转动画即将开始时调用;onTransitionEnd- 页面跳转动画一旦完成会马上调用;
代码示例:
const StackNavigatorConfig = {
initialRouteName: 'Home',
initialRouteParams: {initPara: '初始页面参数'},
navigationOptions: {
title: '标题',
headerTitleStyle: {fontSize: 18, color: '#666666'},
headerStyle: {height: 48, backgroundColor: '#'},
},
paths: 'page/main',
mode: 'card',
headerMode: 'screen',
cardStyle: {backgroundColor: "#ffffff"},
transitionConfig: (() => ({
screenInterpolator: CardStackStyleInterpolator.forHorizontal,
})),
onTransitionStart: (() => {
console.log('页面跳转动画开始');
}),
onTransitionEnd: (() => {
console.log('页面跳转动画结束');
}),
};
navigationOptions 为对应路由页面的配置选项
title- 可以作为头部标题headerTitle,或者Tab标题tabBarLabel;header-自定义的头部组件,使用该属性后系统的头部组件会消失,如果想在页面中自定义,可以设置为null,这样就不会出现页面中留有一个高度为64 navigationBar的高度;headerTitle- 头部的标题,即页面的标题headerBackTitle- 返回标题,默认为title;headerTruncatedBackTitle- 返回标题不能显示时(比如返回标题太长了)显示此标题,默认为 “Back”;headerRight- 头部右边组件;headerLeft- 头部左边组件;headerStyle- 头部组件的样式;headerTitleStyle- 头部标题的样式;headerBackTitleStyle- 头部返回标题的样式;headerTintColor- 头部颜色headerPressColorAndroid - Android 5.0以上MD风格的波纹颜色 ;gesturesEnabled- 否能侧滑返回,iOS默认true,Android默认false;
使用示例
// 注册导航
const Navs = StackNavigator({
Home: { screen: Tabs },
HomeTwo: {
screen: HomeTwo, // 必须, 其他都是非必须
path:'app/homeTwo', // 使用url导航时用到, 如 web app 和 Deep Linking
navigationOptions: {} // 此处参数设置会覆盖组件内的`static navigationOptions`设置. 具体参数详见下文;
},
HomeThree: { screen: HomeThree },
HomeFour: { screen: HomeFour }
}, {
initialRouteName: 'Home', // 默认显示界面
navigationOptions: { // 屏幕导航的默认选项, 也可以在组件内用 static navigationOptions 设置(会覆盖此处的设置)
header: { // 导航栏相关设置项
backTitle: '返回', // 左上角返回键文字
style: {
backgroundColor: '#fff'
},
titleStyle: {
color: 'green'
}
},
cardStack: {
gesturesEnabled: true
}
},
mode: 'card', // 页面切换模式, 左右是card(相当于iOS中的push效果), 上下是modal(相当于iOS中的modal效果)
headerMode: 'screen', // 导航栏的显示模式, screen: 有渐变透明效果, float: 无透明效果, none: 隐藏导航栏
onTransitionStart: ()=>{ console.log('导航栏切换开始'); }, // 回调
onTransitionEnd: ()=>{ console.log('导航栏切换结束'); } // 回调
});
当然页面配置选项 navigationOptions 还可以在对应页面中去静态配置。
// 配置页面导航选项
static navigationOptions = ({navigation}) => ({
title: 'HOME',
titleStyle: {color: '#ff00ff'},
headerStyle:{backgroundColor:'#000000'}
});
render() {
return (
<View></View>
)
};
}
在页面采用静态方式配置 navigationOptions 中的属性,会覆盖 StackNavigator 构造函数中两个参数 RouteConfigs 和 StackNavigatorConfig 配置的 navigationOptions 里面的对应属性。
navigationOptions 中属性的优先级是:
页面中静态配置 > RouteConfigs > StackNavigatorConfig
至此,就可以在组件中直接使用该配置了,当然我们也可以像文章开头提过的那样使用:
const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig);
export default class Main extends Component {
render() {
return (
<Navigator/>
)
};
}
至此,我们已经配置好导航器和对应的路由页面,但是我们还需要navigation才能实现页面的跳转。
navigation 控制页面跳转
在导航器中的每一个页面,都有 navigation 属性,该属性有以下几个属性/方法,可以在组件下console.log(this.props)查看。
navigate- 跳转到其他页面;state- 当前页面导航器的状态;setParams- 更改路由的参数;goBack- 返回;dispatch- 发送一个action;
navigate
this.props.navigation.navigate(‘Two’, { name: ‘two’ }) // push下一个页面
- 1
routeName: 注册过的目标路由名称;params: 传递的参数,传递到下一级界面 ;action:如果该界面是一个navigator的话,将运行这个sub-action;
statestate 里面包含有传递过来的参数 params、key 、路由名称 routeName。
routeName: 路由名;key: 路由身份标识;params: 参数;
setParamsthis.props.navigation.setParams
该方法允许界面更改router中的参数,可以用来动态更改导航栏的内容。比如可以用来更新头部的按钮或者标题。
goBack
返回上一页,可以不传参数,也可以传参数,还可以传 null 。
this.props.navigation.goBack(); // 回退到上一个页面
this.props.navigation.goBack(null); // 回退到任意一个页面
this.props.navigation.goBack('Home'); // 回退到Home页面
dispatch
this.props.navigation.dispatch
可以dispatch一些action,主要支持的action有:
1. Navigate
import { NavigationActions } from 'react-navigation'
const navigationAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
// navigate can have a nested navigate action that will be run inside the child router
action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
})
this.props.navigation.dispatch(navigationAction)
2. ResetReset方法会清除原来的路由记录,添加上新设置的路由信息, 可以指定多个action,index是指定默认显示的那个路由页面, 注意不要越界!
import { NavigationActions } from 'react-navigation'
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Profile'}),
NavigationActions.navigate({ routeName: 'Two'})
]
})
this.props.navigation.dispatch(resetAction)
SetParams
为指定的router更新参数,该参数必须是已经存在于router的param中。
import { NavigationActions } from 'react-navigation'
const setParamsAction = NavigationActions.setParams({
params: {}, // these are the new params that will be merged into the existing route params
// The key of the route that should get the new params
key: 'screen-123',
})
this.props.navigation.dispatch(setParamsAction)
页面跳转,传值,回调传参
跳转,传值
const {navigate} = this.props.navigation;
<TouchableHighlight onPress={()=>{
navigate('PlanDetail',{name:'leslie',id:100});
}}>
// 返回上一页
this.props.navigation.goBack();
在下一界面接收参数
通过 this.props.navigation.state.params 接收参数
export default class Home1 extends Component {
static navigationOptions = {
// title 可以这样设置成一个函数, state 会自动传过来
title: ({state}) => `${state.params.name}`,
};
componentDidMount() {
const {params} = this.props.navigation.state;
const id = params.id;
}
}
回调传参
当前界面进行跳转
navigate('Detail',{
// 跳转的时候携带一个参数去下个页面
callback: (data)=>{
console.log(data); // 打印值为:'回调参数'
}
});
下一界面在返回之前传参
const {navigate,goBack,state} = this.props.navigation;
// 在第二个页面,在goBack之前,将上个页面的方法取到,并回传参数,这样回传的参数会重走render方法
state.params.callback('回调参数');
goBack();
