React Native中应用react-router处理路由




  • 在web开发中,使用地址来标记页面是自然的,但在移动应用,如果我们能够用地址来标记我们的页面,我们也可以获得如下好处:

    1. 页面跳转关系的抽象。使用地址标记页面,当我们需要跳转页面的时候,也只需要声明跳到页面的地址,而无需引入相关组件
    2. 路由的可序列化。这意味着你可以把路由状态保存到持久存储中,在下一次打开应用的时候恢复这个状态。
    3. 更易于进行页面统计(如停留统计等等)两个用户在访问地址相同的页面(同一篇文章、同一个用户的信息等等),我们的后台数据统计就能依据地址来识别哪个页面的访问量多、停留时间长等等。
    4. 能够和web富应用共享更多的代码和逻辑。

    在之前的一个项目中,我们就实践了在React Native中引入react-router作为路由框架。

    在React Native中使用路由框架有以下两种思路:

    1. 模拟一套完整的history API。在React Native中,history的等价概念基本上是navigator,负责维护我当前的访问状态、以及之前的状态(点击后退所回到的页面),如果完成这个封装,我们几乎能使用react-router的全部功能(除了在App中不会体现一个地址栏和不能通过Link渲染<a>外)

    2. 与server-rendering类似,使用react-router的match API,匹配出正确的组件列表进行渲染,而在外层仍然使用navigator进行导航,仅仅是在route对象中用location来取代直接传递Component。这使得我们仍然可以通过控制route来控制sceneConfig,并且还可以在需要的时候传递非字符串类型的属性给子级页面(譬如传递一个回调函数)

    考虑到实现成本以及扩展性,我们暂时在项目中选择的是方案2

    App.js

    应用根组件

    ...
    const INITIAL_ROUTE = {
      location: '/',
    };
    
    export default class App extends React.Component {
      ...
      configureScene = route => {
        if (route.configure) {
          return route.configure;
        }
        if (Platform.OS === 'ios') {
          return Navigator.SceneConfigs.PushFromRight;
        }
        return Navigator.SceneConfigs.FloatFromBottomAndroid;
      };
      renderScene = (route, navigator) => (
        <PageContainer
           extraProps={route.extraProps}
           location={route.location} 
           navigator={navigator}
           rootBackAndroid={this.onBackAndroid}
        />
      );
      render() {
        return (
          <Provider store={store} key="provider">
            <Navigator
              ref="navigator"
              initialRoute={INITIAL_ROUTE}
              configureScene={this.configureScene}
              renderScene={this.renderScene}
            />
          </Provider>
        );
      }
    }
    

    PageContainer.js

    页面容器。在这个组件的状态里维护路由匹配的结果,并渲染正确的组件。

    import React, {
      View,
      PropTypes,
    } from 'react-native';
    import { match } from 'react-router';
    import routesConfig from '../routes';
    
    export default class PageContainer extends React.Component {
      componentWillMount() {
        this.doMatch(this.props);
      }
      componentWillReceiveProps(newProps) {
        if (newProps.location !== this.props.location) {
          this.doMatch(newProps);
        }
      }
      doMatch(props) {
        match({
          location,
          routes: routesConfig,
        }, (err, redirectLocation, renderProps) => {
          this.setState({ routerState: renderProps });
        });
      }
      render() {
        if (this.state.routerState) {
          const {
            routes,
            params,
          } = this.state.routerState;
          return [...routes].reduceRight((children, route) => {
            const Component = route.component;
            return Component ? (
              <Component
                route={route}
                navigator={this.props.navigator}
                params={params}
                {...this.props.extraProps}
              >
                {children}
              </Component>
            ) : children;
          }, null);
        }
        return <View />;
      }
    }
    

    routes.js

    路由定义,这部分内容可以与web版公用,所以可以拆为单独文件。

    module.exports = {
      path: '/',
      indexRoute: {
        component: Index,
      },
      childRoutes: [
        {
          path: '/article/:id',
          component: Article
        },
      ],
    };
    

    使用:

    我们可以这样压入一个页面,navigator本身的使用方法还是与之前一样:

    navigator.push({
      location: '/article/' + article.id,
    })
    

    可以在Article组件中通过this.props.params.id获取到:id参数

    可以这样查看顶部的页面地址:

    const routers = navigator.getCurrentRoutes();
    const {location} = routers[routers.length - 1];
    


  • 正好需要,学习了,谢谢



  • 大赞,学习~~:clap:



  • 能有全一点的code 嘛。 省略很多,新手理解困难:joy:



  • 此回复已被删除!


  • 能给个小demo吗