新手理解Navigator的教程




  • 这是一个简单的例子,用Navigator来跳转页面,页面之间传递参数 (代码是ES6语法写的):

            
        import React from 'react';
        import {
            View,
            Navigator
        } from 'react-native';
        import FirstPageComponent from './FirstPageComponent';
        
        export default class SampleComponent extends React.Component {
            render() {
                let defaultName = 'FirstPageComponent';
                let defaultComponent = FirstPageComponent;
                return (
                <Navigator
                  initialRoute={{ name: defaultName, component: defaultComponent }}
                  configureScene={(route) => {
                    return Navigator.SceneConfigs.VerticalDownSwipeJump;
                  }}
                  renderScene={(route, navigator) => {
                    let Component = route.component;
                    return <Component {...route.params} navigator={navigator} />
                  }} />
                );
            }
        } 
    

    这里来解释一下代码:

    第三行: 一个初始首页的component名字,比如我写了一个component叫HomeComponent,那么这个name就是这个组件的名字【HomeComponent】了。

    第四行: 这个组件的Class,用来一会儿实例化成 <Component />标签

    第七行: initialRoute={{ name: defaultName, component: defaultComponent }} 这个指定了默认的页面,也就是启动app之后会看到界面的第一屏。 需要填写两个参数: name 跟 component。(注意这里填什么参数纯粹是自定义的,因为这个参数也是你自己发自己收,自己在renderScene方法中处理。我们这里示例用了两个参数,但其实真正使用的参数只有component)

    第八,九,十行: configureScene={() => {
    return Navigator.SceneConfigs.VerticalDownSwipeJump;
    }} 这个是页面之间跳转时候的动画,具体有哪些?可以看这个目录下,有源代码的: node_modules/react-native/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js

    最后的几行: renderScene={(route, navigator) => {
    let Component = route.component;
    return <Component {...route.params} navigator={navigator} />
    }} />
    );

    这里是每个人最疑惑的,我们先看到回调里的两个参数:route, navigator。通过打印我们发现route里其实就是我们传递的name,component这两个货,navigator是一个Navigator的对象,为什么呢,因为它有push pop jump...等方法,这是我们等下用来跳转页面用的那个navigator对象。

    return <Component {...route.params} navigator={navigator} />
    

    这里有一个判断,也就是如果传递进来的component存在,那我们就是返回一个这个component,结合前面 initialRoute 的参数,我们就是知道,这是一个会被render出来给用户看到的component,然后navigator作为props传递给了这个component。

    所以下一步,在这个FirstPageComponent里面,我们可以直接拿到这个 props.navigator:

    import React from 'react';
    import {
        View,
        Navigator
    } from 'react-native';
    
    import SecondPageComponent from './SecondPageComponent';
    
    export default class FirstPageComponent extends React.Component {
        constructor(props) {
            super(props);
            this.state = {};
        }
        _pressButton() {
            const { navigator } = this.props;
            //为什么这里可以取得 props.navigator?请看上文:
            //<Component {...route.params} navigator={navigator} />
            //这里传递了navigator作为props
            if(navigator) {
                navigator.push({
                    name: 'SecondPageComponent',
                    component: SecondPageComponent,
                })
            }
        }
        render() {
            return (
                <View>
                    <TouchableOpacity onPress={this._pressButton.bind(this)}>
                        <Text>点我跳转</Text>
                    </TouchableOpacity>
                </View>
            );
        }
    }
    

    这个里面创建了一个可以点击的区域,让我们点击可以跳到SecondPageComponent这个页面,实现页面的跳转。
    现在来创建SecondPageComponent,并且让它可以再跳回FirstPageComponent:

    import React from 'react';
    import {
        View,
        Navigator
    } from 'react-native';
    
    import FirstPageComponent from './FirstPageComponent';
    
    export default class SecondPageComponent extends React.Component {
    
        constructor(props) {
            super(props);
            this.state = {};
        }
    
        _pressButton() {
            const { navigator } = this.props;
            if(navigator) {
                //很熟悉吧,入栈出栈~ 把当前的页面pop掉,这里就返回到了上一个页面:FirstPageComponent了
                navigator.pop();
            }
        }
    
        render() {
        return (
                <View>
                    <TouchableOpacity onPress={this._pressButton.bind(this)}>
                        <Text>点我跳回去</Text>
                    </TouchableOpacity>
                </View>
        );
        }
    }
    

    大功告成,能进能出了。

    关于官方文档里有个东西,这里说一下:

    getCurrentRoutes() - 获取当前栈里的路由,也就是push进来,没有pop掉的那些
    jumpBack() - 跳回之前的路由,当然前提是保留现在的,还可以再跳回来,会给你保留原样。
    jumpForward() - 上一个方法不是调到之前的路由了么,用这个跳回来就好了
    jumpTo(route) - Transition to an existing scene without unmounting
    push(route) - Navigate forward to a new scene, squashing any scenes that you could jumpForward to
    pop() - Transition back and unmount the current scene
    replace(route) - Replace the current scene with a new route
    replaceAtIndex(route, index) - Replace a scene as specified by an index
    replacePrevious(route) - Replace the previous scene
    immediatelyResetRouteStack(routeStack) - Reset every scene with an array of routes
    popToRoute(route) - Pop to a particular scene, as specified by its route. All scenes after it will be unmounted
    popToTop() - Pop to the first scene in the stack, unmounting every other scene
    

    这些都是navigator可以用的public method,就是跳转用的,里面有些带参数的XXX(route),新手第一次看这个文档会疑惑,这个route参数是啥呢,这个route就是:

    renderScene={(route, navigator) => 
    

    这里的route,最基本的route就是:

    var route = {
        component: LoginComponent
    }
    

    这种格式。这个地方有点模糊的,在这里先说清楚了。

    然后下面要讨论,怎么传递参数过去,或者从对方获取参数。
    传递参数,通过push就可以了。
    比如在一个 press的事件里:

    //FirstPageComponent.js
    import React from 'react';
    import {
        View,
        Navigator
    } from 'react-native';
    
    import SecondPageComponent from './SecondPageComponent';
    
    export default class FirstPageComponent extends React.Component {
    
        constructor(props) {
            super(props);
            this.state = {
                id: 2
            };
        }
    
    
        _pressButton() {
            const { navigator } = this.props;
            if(navigator) {
                navigator.push({
                    name: 'SecondPageComponent',
                    component: SecondPageComponent,
                    //这里多出了一个 params 其实来自于<Navigator 里的一个方法的参数...
                    params: {
                        id: this.state.id
                    }
                });
            }
        }
    
        render() {
            return (
                <View>
                    <TouchableOpacity onPress={this._pressButton.bind(this)}>
                        <Text>点我跳转并传递id</Text>
                    </TouchableOpacity>
                </View>
            );
        }
    }
    

    params的来历:

    // index.ios.js
    <Navigator
      initialRoute={{ name: defaultName, component: defaultComponent }}
      configureScene={() => {
        return Navigator.SceneConfigs.VerticalDownSwipeJump;
      }}
      renderScene={(route, navigator) => {
        let Component = route.component;
        if(route.component) {
            //这里有个 { ...route.params }
            return <Component {...route.params} navigator={navigator} />
        }
      }} />
    

    这个语法是把 routes.params 里的每个key 作为props的一个属性:

    navigator.push({
        name: 'SecondPageComponent',
        component: SecondPageComponent,
        params: {
            id: this.state.id
        }
    });
    

    这里的 params.id 就变成了 <Navigator id={} 传递给了下一个页面。
    所以 SecondPageComponent就应该这样取得 id:

    //SecondPageComponent.js
    import React from 'react';
    import {
        View,
        Navigator
    } from 'react-native';
    
    import FirstPageComponent from './FirstPageComponent';
    
    export default class SecondPageComponent extends React.Component {
    
        constructor(props) {
            super(props);
            this.state = {
                id: null
            }
        }
    
        componentDidMount() {
            //这里获取从FirstPageComponent传递过来的参数: id
            this.setState({
                id: this.props.id
            });
        }
    
        _pressButton() {
            const { navigator } = this.props;
            if(navigator) {
                navigator.pop();
            }
        }
    
        render() {
            return (
                <View>
                    <Text>获得的参数: id={ this.state.id }</Text>
                    <TouchableOpacity onPress={this._pressButton.bind(this)}>
                        <Text>点我跳回去</Text>
                    </TouchableOpacity>
                </View>
            );
        }
    }
    

    这样在页面间传递的参数,就可以获取了。


    @sunnylqm 很多人不理解这里的params,我忍不住稍微补充一下。
    0_1453170797112_QQ图片20160119102616.png


    然后就是返回的时候,也需要传递参数回上一个页面:
    但是navigator.pop()并没有提供参数,因为pop()只是从 [路由1,路由2,路由3。。。]里把最后一个路由踢出去的操作,并不支持传递参数给倒数第二个路由,这里要用到一个概念,把上一个页面的实例或者回调方法,作为参数传递到当前页面来,在当前页面操作上一个页面的state:

    这是一个查询用户信息的例子,FirstPageComponent传递id到SecondPageComponent,然后SecondPageComponent返回user信息给FirstPageComponent

    //FirstPageComponent.js
    import React from 'react';
    import {
        View,
        Navigator
    } from 'react-native';
    
    import SecondPageComponent from './SecondPageComponent';
    
    export default class FirstPageComponent extends React.Component {
    
        constructor(props) {
            super(props);
    
            this.state = {
                id: 2,
            user: null,
            }
        }
    
    
        _pressButton() {
            let _this = this;
            const { navigator } = this.props;
            if(navigator) {
                navigator.push({
                    name: 'SecondPageComponent',
                    component: SecondPageComponent,
                    params: {
                        id: this.state.id,
                        //从SecondPageComponent获取user
                        getUser: function(user) {
                            _this.setState({
                                user: user
                            })
                        }
                    }
                });
            }
        }
    
        render() {
            if( this.state.user ) {
                return(
                    <View>
                        <Text>用户信息: { JSON.stringify(this.state.user) }</Text>
                    </View>
                );
            }else {
                return(
                    <View>
                        <TouchableOpacity onPress={this._pressButton.bind(this)}>
                            <Text>查询ID为{ this.state.id }的用户信息</Text>
                        </TouchableOpacity>
                    </View>
                );
            }
    
        }
    }
    

    然后再操作SecondPageComponent:

    //SecondPageComponent.js
    
    const USER_MODELS = {
        1: { name: 'mot', age: 23 },
        2: { name: '晴明大大', age: 25 }
    };
    
    import React from 'react';
    import {
        View,
        Navigator
    } from 'react-native';
    
    import FirstPageComponent from './FirstPageComponent';
    
    export default class SecondPageComponent extends React.Component {
    
        constructor(props) {
            super(props);
            this.state = {
                id: null
            }
        }
    
        componentDidMount() {
            //这里获取从FirstPageComponent传递过来的参数: id
            this.setState({
                id: this.props.id
            });
        }
    
        _pressButton() {
                const { navigator } = this.props;
                
                if(this.props.getUser) {
                    let user = USER_MODELS[this.props.id];
                    this.props.getUser(user);
                }
                    
                if(navigator) {
                    navigator.pop();
                }
        }
    
        render() {
            return(
                <View>
                    <Text>获得的参数: id={ this.state.id }</Text>
                    <TouchableOpacity onPress={this._pressButton.bind(this)}>
                        <Text>点我跳回去</Text>
                    </TouchableOpacity>
                </View>
            );
        }
    }
    

    看下效果如何吧。

    放个类似的例子代码: https://github.com/mozillo/navigation
    安装方法: npm install && react-native run-android



  • 能不能给代码加个行号啊。



  • @小木匠ang 这个要论坛支持codehighlight



  • @mozillo 哦哦,我以为你是建论坛的人里的呢。只是建议下,其实有没有也无所谓



  • return <Component {...route.params} navigator={navigator} />
    ...是省略的意思吗?省略了哪些属性?



  • @星星 这是展开的语法 route.params就是后面你 push时候

    push({
      name: 'Home',
      component: Home,
      params: {
        id: 100,
       ...
      }
    })
    

    这里的东西,类似url里的 ?params[id]=100



  • @mozillo 说:

    是RN提供的一个路由工具,不过缺陷是没有预设一个路由配置文件,

    @小木匠ang



  • @messi 这是我刚学rn时候的感觉,我也不是很了解,像我开发的时候,一般会创建一个router对象,因为有配置路由再开发MVC的习惯,会在router里写一些路由,最后像router.openHome(props)或者router.openProfile(props)这样去调用



  • @mozillo 说:

    r defaultComponen

    代码里面的export可以也写出来吗?新手表示看晕


  • administrators



  • @JYoung_Li 说:

    好,周末我再精简一下



  • Navigator里的手势只能用于跳转已经创建的Page, 不知道能不能有什么trick可以用向右的手势来创造(push或者replace)新的page


  • administrators

    @Kiri 可以自己处理手势然后调用navigator.push或replace



  • 有什么办法能用Navigator外的控件来控制Navigate的转跳吗,比如

    <Navigator ... />
    <Button onPress={/*控制Navigator内的页面转跳*/} />
    

  • administrators

    @LzxHahaha 将Navigator暴露出来,比如

    <Navigator ref={nav => global.nav = nav} ... />
    <Button onPress={/* nav.push/pop  */} />
    


  • @sunnylqm 嗯,问题解决了,谢谢



  • 此回复已被删除!


  • 此回复已被删除!


  • 可以给个demo吗 楼主



  • 绝赞~!学习了!



  • 您好,如果在RN应用中要调用一个OC方法,并且方法中需要传入一个视图控制器参数,这种情况在RN中应该如何处理呢?


登录后回复