RN学习笔记之Navigator

作者: AnonyPer | 来源:发表于2017-04-28 13:00 被阅读80次

    接触了RN之后,必不可免得要接触界面之间跳转之类的需求,而这一类需求的实现必须要使用到Navigator这个导航器,这次记录一下使用过程中对于Navigator导航器的认知。

    首先要理解这个导航器,可以通俗的理解和Android中activity的堆栈管理一样,导航器除了界面导航功能之外,还提供界面栈的管理,界面的跳入和跳出。(RN中每一个component都相当于一个组件,一个或多个component共同构成场景(Scene),场景通俗的理解就是一个占据整个屏幕的界面)

    RN中的入口是index.android.js(以Android为例),这个index.js可以看成整个RN组建的框架,一些基础的东西都在这里实例化、定义。我们的导航器也需要在这个文件中被创建。(后文中提到的BackAndroid也在这里面定义)

    Navigator属性介绍

    Navigator中包含如下属性:

    initialRoute

    初始化路由,初始化需要显示的Component,其中的component参数必须要有,定义如下:

    initialRoute={{title:'main',id:'main',component:defaultComponent}}

    configureScene

    配置场景动画,系统提供了很多动画,如从底部弹出,从左弹出等,参数如下:

    组件中定义:

    configureScene={this._configureScence.bind(this)}

    _configureScence(route) {

    console.log("AndroidTestComponent=====configureScenceAndroid"+ route.type)

    if(route.type =='Bottom') {

    returnNavigator.SceneConfigs.FloatFromBottom;// 底部弹出

    }else if(route.type =='Left') {

    returnNavigator.SceneConfigs.FloatFromLeft// 右侧弹出

    }else if(route.type =='Right') {

    returnNavigator.SceneConfigs.FloatFromRight//左侧弹出

    }

    returnNavigator.SceneConfigs.PushFromRight;// 默认右侧弹出

    }

    renderScene

    场景渲染,根据路由来确定要挂载渲染的场景,设置如下:

    组件中定义

    renderScene={this._scene.bind(this)}

    //场景渲染方法,传入路由器和导航器两个方法

    _scene(route, navigator) {

    console.log(route)

    //这个里面如果不做处理,默认返回的是initialRoute初始化的component

    letComponent= route.component;

    //路由器的params可以携带参数

    //将改导航器传递给下一个Component

    return

    //或者直接引入一个现成的Component

    //return

    }

    ref

    这个属性有点很微妙,网上很多介绍Navigator的博客代码中没有写这个属性,这个属性相当于给一个组件添加一个label标签,然后通过该标签可以找到对应的组件,发现这个属性的原因是我在写BackAndroid的时候,需要使用到navigator这个对象,在监听物理返回键的时候判断是否还有路由存在,通常在方法中获取navigator的方法如下:

    _pressButton(){

    const {navigator} = this.props;

    }

    这么写的前提是_pressButton该方法一般都会被bind,而且该Component在挂载前已经把navigator传递过来了,所以可以获得到,但是我们在index.js中使用BackAndroid,定义方法不管是使用箭头函数或者在构造方法中bind对应的方法,这个时候this.props都没有navigator这个属性,所以这个时候是找不到的,也就没办法实现导航回退的功能,而使用ref就很好的解决这个问题了,即子组件获取父组件通过props、父组件获取子组件通过refs。如下设置:

    在组建中添加:

    ref="navigator"

    方法中调用:

    onBackAndroid=()=>{

    constnavigator=this.refs.navigator;

    ...

    }

    Navigator方法

    getCurrentRoutes() - 获取当前栈里的路由列表,也就是push进来,没有pop掉的那些

    jumpBack() - 跳回之前的路由,保留现在的,还可以再跳回来。相当于浏览器的回退

    jumpForward() - 结合jumpBack,此方法再重新打开回退前的,相当于浏览器的前进

    jumpTo(route) - 跳转到一个没有被取消挂载的已存在场景

    push(route) - push一个新的路由场景

    pop() - 移除并取消挂载当前的场景,回到上一个场景

    replace(route) -用一个新的路由场景替代当前的场景,该方法之后当前的场景就被取消挂载了

    replaceAtIndex(route,index) -通过制定index下标replace

    replacePrevious(route) -replace前一个场景

    immediatelyResetRouteStack(routeStack) -用新的路由场景Stack重置堆栈中的每一个场景

    popToRoute(route) - 移除并取消挂载当前场景到制定场景之间的对

    popToTop() - 移除并取消挂载出堆栈中第一个场景外的其他场景

    其中route路由最基本的就是

    var route = {component: LoginComponent}

    完整代码如下

    //component是从react中来的

    importReact, {Component}from'react';

    //Text以及View等都是从react-native中来的

    import{

    AppRegistry,

    StyleSheet,

    Navigator,

    BackAndroid,

    Dimensions

    }from'react-native';

    importsplashfrom'./app/mainview/splash'

    importguidefrom'./app/mainview/guide'

    //定义一个Component,按照ES6的语法来,就和java语法中定义class一样,继承component

    export  default  classAndroidTestComponentextendsComponent{

    //构造函数

    constructor(props) {

    super(props)

    //如果_onBackAndroid不是一个箭头函数,需要在构造函数中bind this,要不然在添加监听和移除监听时操作的对象是不同的

    // this._onBackAndroid = this.onBackAndroid.bind(this)

    }

    //场景动画

    _configureScence(route) {

    console.log("AndroidTestComponent=====configureScenceAndroid"+ route.type)

    if(route.type =='Bottom') {

    returnNavigator.SceneConfigs.FloatFromBottom;// 底部弹出

    }else if(route.type =='Left') {

    returnNavigator.SceneConfigs.FloatFromLeft// 右侧弹出

    }else if(route.type =='Right') {

    returnNavigator.SceneConfigs.FloatFromRight//左侧弹出

    }

    returnNavigator.SceneConfigs.PushFromRight;// 默认右侧弹出

    }

    //场景渲染

    _scene(route, navigator) {

    letComponent= route.component;

    //传递参数以及导航器

    return

    }

    //使用箭头函数,直接绑定this,不需要再构造函数中再去bind

    onBackAndroid=()=>{

    //使用refs来获取导航器

    constnavigator=this.refs.navigator;

    if(!navigator){

    return false;

    }

    constrouters=navigator.getCurrentRoutes();

    if(routers.length>1){

    navigator.pop();

    return true;

    }else{

    return false;

    }

    }

    //compoment将要挂载的函数,这个时候可以在继续更新state 添加监听

    componentWillMount() {

    console.log("AndroidTestComponent=====componentWillMount")

    BackAndroid.addEventListener('hardwareBackPress',this.onBackAndroid)

    }

    //render属性对应的函数会返回一段JSX来表示该组件的结构和布局。该部分是一个组件必不可少的地方,没有这些内容,就无法构成一个组件。

    //render方法必须返回单个根元素

    //compoment挂载渲染的函数

    render() {

    //定义默认闪屏界面

    letdefaultComponent= splash;

    return(

    configureScene={this._configureScence.bind(this)}

    renderScene={this._scene.bind(this)}

    ref="navigator"

    />

    );

    }

    //compoment已经挂载的函数

    //界面渲染完之后,在进行一些数据处理,比如网络数据加载,比如本地数据加载

    componentDidMount() {

    console.log("AndroidTestComponent=====componentDidMount")

    }

    //作为子控件时,当期属性被改变时调用

    componentWillReceiveProps(nextProps) {

    console.log("AndroidTestComponent=====componentWillReceiveProps")

    }

    //component将要更新时调用

    componentWillUpdate(nextProps, nextState) {

    console.log("AndroidTestComponent=====componentWillUpdate")

    }

    //component更新后调用

    componentDidUpdate(prevProps, prevState) {

    console.log("AndroidTestComponent=====componentDidUpdate")

    }

    //component销毁时调用

    componentWillUnmount() {

    console.log("AndroidTestComponent=====componentWillUnmount")

    BackAndroid.removeEventListener('hardwareBackPress',this.onBackAndroid)

    }

    }

    conststyles=StyleSheet.create({

    container: {

    flex:1,

    justifyContent:'flex-start',

    alignItems:'stretch',

    backgroundColor:'white'

    },

    lineStyle: {

    backgroundColor:'grey',

    height:0.3,

    },

    loadText: {

    fontSize:20,

    textAlign:'center',

    margin:10

    },

    loadView: {

    flex:1,

    alignItems:'center',

    justifyContent:'center'

    },

    });

    //另一种定义props的方法,如果static defaultProps也定义了,这个会覆盖上面的

    // AndroidTestComponent.defaultProps = {

    //    name:'xiaoerlang'

    // }

    //进行注册 'RNProject'为项目名称 AndroidTestComponent 为启动的component

    AppRegistry.registerComponent('RNProject', () => AndroidTestComponent);

    过程中遇到的问题及解决方案:

    react native - expected acomponent class, got [object Object]

    该错误是引用了小写的组件,组件首字母一定要大写,比如<splash/>应该写成<Splash>

    相关文章

      网友评论

        本文标题:RN学习笔记之Navigator

        本文链接:https://www.haomeiwen.com/subject/yjjnzttx.html