从React-Navigation开始
如何创建一个iOS 上面NavigationController + UITabBarController的架构?
直接开始撸代码,撸完再解释:
今天目标是:
1.创建一个工程(今天是2019年4月1日,是最新的React-native version是0.59)
2.查看工程目录
3.导入React-Navigation
4.开始项目编写主界面和子页面
5.开始搭建NavigationController + UITabBarController架构
6.运行项目
7.总结
1.创建一个工程(今天是2019年4月1日,是最新的react-native version是0.59)
到你想创建工程的文件夹路径下,执行如下代码
/**
* react-native init 固定写法,初始化
* NavigationProject 工程名字,随便起,无关大雅
*/
$react-native init NavigationProject
注意:网络好的情况下几十秒就创建完成了
2.查看工程目录
android:这是android项目的入口
iOS:这是iOS项目的入口
node_modules:用于存放node.js包,里面是一些js的库
.js:项目文件,用于存放逻辑处理,界面代码的
.json:配置性的文件
注:跨平台项目都会生成多个工程入口去对应不同的平台
index.js:相当于原生开发中的main文件,工程的入口,App.js就是一个界面(这里用的是vs code)
3.导入React-Navigation
拉起vscode底下蓝色状态栏目部分,选择TERMINAL这一项,这里相当于你的终端,输入:
$yarn add react-navigation
# 或者使用 npm
# $npm install --save react-navigation
等待下载完毕,查看yarn.lock文件中是否存在了React-navigation相关的配置
下列两个步骤一定要去添加
$yarn add react-native-gesture-handler
# or with npm
# $npm install --save react-native-gesture-handler
Link的原生依赖
$react-native link react-native-gesture-handler
对于Android工程,在Android中的MainActivity.java
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
};
}
如果开发混编App需要到工程里面去做相应的配置,请参考官网
++ https://reactnavigation.org/docs/zh-Hans/getting-started.html ++
代码编写
1.stack navigator的使用
stack navigator:简单理解就是iOS中的UINavigationController,负责页面的跳转,导航;是一个先进后出的栈。
1.配置一个NavigationContainer
通过:createStackNavigator()这个方法来创建一个NavigationContainer
// 源码
export function createStackNavigator(
routeConfigMap: NavigationRouteConfigMap,
stackConfig?: StackNavigatorConfig
): NavigationContainer;
创建一个NavigationContainer需要两个参数:
routeConfigMap:路由配置地图,简单来说可以预先添加几个界面
// 通过源码来查看routeConfigMap
// NavigationRouteConfigMap
export interface NavigationRouteConfigMap {
[routeName: string]: NavigationRouteConfig;
}
// NavigationRouteConfig
export type NavigationRouteConfig =
| NavigationComponent
| ({
navigationOptions?: NavigationScreenConfig<any>;
path?: string;
} & NavigationScreenRouteConfig
);
// NavigationScreenRouteConfig
export type NavigationScreenRouteConfig =
| NavigationComponent
| {
screen: NavigationComponent;
}
| {
getScreen: () => NavigationComponent;
};
由源码我们可以知道routeConfigMap是一个字符串:一个路由配置构成
可以简单理解为
{字符串(表示路由名字):路由配置}
NavigationRouteConfig:配置是的可以接收三种类型的入参
第一种,传入一个extends NavigationComponent的组建
第二种, 传入{navigationOptions?:NavigationScreenConfig<any>;path?: string};
NavigationScreenConfig:导航页面的相关配置
path:路径
第三种,一个返回NavigationComponent组建的方法
stackConfig?:栈的配置
// StackNavigatorConfig
export interface StackNavigatorConfig
extends NavigationStackViewConfig,
NavigationStackRouterConfig {
containerOptions?: any;
}
// NavigationStackViewConfig
export interface NavigationStackViewConfig {
mode?: 'card' | 'modal';
headerMode?: HeaderMode;
headerBackTitleVisible?: boolean;
headerTransitionPreset?: 'fade-in-place' | 'uikit';
headerLayoutPreset?: 'left' | 'center';
cardShadowEnabled?: boolean;
cardOverlayEnabled?: boolean;
cardStyle?: StyleProp<ViewStyle>;
transparentCard?: boolean;
transitionConfig?: (
transitionProps: NavigationTransitionProps,
prevTransitionProps: NavigationTransitionProps,
isModal: boolean
) => TransitionConfig;
onTransitionStart?: (
transitionProps: NavigationTransitionProps,
prevTransitionProps?: NavigationTransitionProps
) => Promise<void> | void;
onTransitionEnd?: (
transitionProps: NavigationTransitionProps,
prevTransitionProps?: NavigationTransitionProps
) => void;
}
// NavigationStackRouterConfig
export interface NavigationStackRouterConfig {
headerTransitionPreset?: 'fade-in-place' | 'uikit';
initialRouteName?: string;
initialRouteParams?: NavigationParams;
paths?: NavigationPathsConfig;
defaultNavigationOptions?: NavigationScreenConfig<NavigationScreenOptions>;
navigationOptions?: NavigationScreenConfig<NavigationScreenOptions>;
initialRouteKey?: string;
}
一个简单例子
1.创建两个界面Index(index.js),Details(details.js)
2.编写App.js,至于Index.js(这个是系统的,为了和系统区分,我这里用了一个index,小写的)无需做任何更改;
// 导入createAppContainer, createStackNavigator两个方法
import { createAppContainer, createStackNavigator } from 'react-navigation';
// 这是我创建的两个界面
import Index from './component/index/index.js';
import Details from './component/details/details.js';
// 通过createStackNavigator创建一个NavigationContainer
const AppNavigator = createStackNavigator({
Home: {
screen: Index,
},
Details: {
screen: Details,
},
}, {
initialRouteName: 'Home',
});
// 通过createAppContainer创建一个可以导出的NavigationContainer
export default createAppContainer(AppNavigator);
上述的操作,让很多无法理解createStackNavigator()和createAppContainer()创建出来的类型都是一样的
理论上我们可以写下这样代码:
import React, {Component} from "react"
import { createAppContainer, createStackNavigator } from 'react-navigation';
import Index from './component/index/index.js';
import Details from './component/details/details.js';
const AppNavigator = createStackNavigator({
Home: {
screen: Index,
},
Details: {
screen: Details,
},
}, {
initialRouteName: 'Home',
});
export default class App extends Component {
render(){
return (
<AppNavigator />
);
}
}
// export default createAppContainer(AppNavigator);
啪一下,运行报错了
报错的信息,是
Invariant Violation: The navigation prop is missing for this navigator. In react-navigation 3 you must set up your app container directly.
换言之,就是固定写法,react-navigation 3采用的第一种方式,我当时的想法是错误的。
index.js
import React, {Component} from 'react'
import {
View,
Text,
StyleSheet
} from 'react-native'
export default class Index extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions }
onPress={()=>{this.props.navigation.navigate('Details')}}
>To get started, edit Index.js</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
details.js
import React, {Component} from 'react'
import {
View,
Text,
StyleSheet
} from 'react-native'
export default class Details extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}
onPress={()=>{this.props.navigation.navigate('Home')}}
>go to home </Text>
<Text style={styles.instructions}
onPress={()=>this.props.navigation.goBack()}
> go back </Text>
<Text style={styles.instructions}
onPress={()=>this.props.navigation.push('Details')}
> go to new details </Text>
<Text style={styles.instructions}
onPress={()=>this.props.navigation.navigate('Details')}
> go to details </Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
查看结果:
我们可以打印一下this.props
// 在render()方法返回之前添加打印代码
console.log("\n details this.props", this.props);
这里我们可以看见在props中有navigation的属性,换言之,在栈里面的页面就会有一个有值的navigation属性
安利一个调试方式
网上很多教程使用chrome浏览器进行调试,对于小白来说这种调试方式很不友好,很多iOS或者Android转过来的童鞋们还是习惯的IDE里面去看报错信息。让vs code可以打印js log
1.调试操作
2.添加调试