react-native里面用于导航的最知名的库就是react-navigation,它非常优雅的把native界面间的切换抽象为几种形式,并且采用配置来解决react-native自带导航组件使用复杂的问题。本文并不详细去介绍react-navigation这个组件的使用方法,建议你先阅读它的官方文档,并且体验一下它的examples之后,再来阅读本文。本文主要是用以梳理react-navigation导航的设计思维,以及给出一些实际开发中,如何安排几种不同形式的导航的组合策略。
导航的本质
和web界面不同,web界面直接通过url确定界面,通过<a>标签实现界面之间的切换。即使在SPA时代,也是通过比较轻松的router组件实现界面切换。但是native里面,特别是react-native开发里面,界面切换需要native层面手动实现,因此,一定需要有人站出来,把界面切换抽象为普适性的一种规则。react-navigation的基本思想,就是一个react-native app的所有界面(screen)都是素材,通过navigation这个导航器进行编制,让他们可以自由切换。
react-navigation的本质,就是建立一个路由网络的索引,并且在建立的时候,就规定好每一个索引节点的表现形式。而界面(screen)只是这些节点下面的素材,一个screen可以被引用到不同的节点下面,再加上节点上的配置信息,就可以产生不同的导航效果。而这个节点的全部信息加起来,就是一个navigation,而一组同一层面的navigation又可以放在一起,再附加一些配置信息,这些配置信息可能对所有的navigation都有效,那么这些navigation的集合就被称为一个navigator。navigator有不同层面的,也就是说,一个navigator里面,可以把另外一个navigator作为一个navigationSetNode(特殊节点)。这样,所有被引用到的screen都被管理在react-navigation建立的作用域下,这样,只要知道节点的名称,就可以立即导航到这个界面。
四种导航形式
react-navigation里面有四种导航形式,分别是:stack navigator、tab navigator、drawer navigator、full-screen modal。下面一一简单阐述一下,不作深入的解释。
Stack Navigator(栈导航)
栈的特点就是“先进后出,后进先出”,就是有顺序的堆叠态。因此,在stack navigator里的所有节点都是按照线性排列的,于是就有go back这样的操作。除了stack navigator,其他形式都没有go back操作(modal有close操作),因为没有stack。
stack navigator的另外一个特点,就是它可以有header bar以及header bar里面的go back button。这个特点其他形式也没有,特别是tab navigator和drawer navigator,这点在开发中要非常注意。modal我们后面会细说。对于一个stack navigator而言,它的header title只会在一个navigation创建的时候形成,一旦形成,就不会更改,这点算是坑,如果你在一个stack里面,把两个screen设置为同一个组件,又没有设置特定的headerTitle属性,你会发现,这两个screen虽然属于不同navigation,但是title却是一样的。
Tab Navigator(选项卡导航)
也就是我们常见的底部菜单那种形式。它几乎没有什么特点,唯一的特点(也是缺点)是没有header bar,这点要注意。也正因如此,一般一个app的顶层导航,不会使用tab navigator,除非它超级简单,除了这几个tab没有其他页面。也正因如此,我们一般会在它的里面使用stack navigator作为子节点,这样才能既有tab bar,又有header bar,还能保证每个tab的header title不一样。
Drawer Navigator(抽屉导航)
常见的左侧导航,点击一个按钮从左侧飞出来,点一下又收起来关闭。它在很多方面很像tab navigator,只是不在界面上占用视觉空间而已。
Full-screen Modal(全屏弹出层)
一定要加上“full-screen”,是因为这种modal和那种仅在一个小范围内遮罩展示的modal不同,这种modal的本质是一个navigation,而且这个navigation总是处于一个stack的最顶层,你不能再在这个stack的顶上加新层,你必须先关闭modal之后,才可以再在这个stack上加新层。说白了,full-screen modal其实是穿着modal外衣的screen。
在使用时,你需要咋navigator options里面传入:
mode: 'modal', // 启用modal模式,这样,所有的navigator将会从屏幕下方出现 headerMode: 'none', // 隐藏modal的header,因为如果你使用header,会和stack里面的header同时出现(2个header的高度)
它有如下几个特点:
- 处于某个stack的最顶层
- 打开方式(进入效果)不同
- 当在modal内部导航到stack内的另外层时,它自己会先被关闭
- 写代码时,它一定和一个stackNavigatorSetNode放在一起,置于一个stack navigator中(当然,这个navigator允许有其他的modal)
这里打一个比方,你现在手上有一副扑克牌,你把大小王拿出来,它们就是full-screen modal,你把它们放在其他牌的外面区分开。现在开始游戏:你将除大小王之外的其他所有牌拿在手中,洗牌,牌盒就是一个stack,你一次只能放入一张手牌,放入的牌是随机的,每次放入的必须置于已经放入的牌的最顶端,想要从牌盒内拿回牌,只能拿取最顶上一张;当你使用大小王替代手牌放入的时候,你不能继续在王牌上放其他牌(包括另外一张王牌);如果你想继续游戏,就必须将已经放入的王牌拿出,弃于王牌堆内,使牌盒内恢复普通牌堆形式。
导航策略
虽然这种抽象挺有意思,也符合理论上的逻辑。但是实际开发中,我们总会遇到这样那样的坑,那么怎样才能保证在开发时,不至于乱用而达不到自己想要的效果呢?
我们要遵循以下策略:
- 仅在作为screen的组件中使用this.props.navigation,它们的子组件里面不能使用,因此,将这些被react-navigation直接使用的组件命名为xxx.screen.js
- 将app最顶层的导航设定为mode: 'modal',并且将所有的modal都放在这一层
- 业务流放在stack navigator
- stack navigator作为tab/drawer navigator的子节点,通过stack里面的screen组件navigationOptions配置header title和是否显示tabbar
- 将tab/drawer navigator作为stack navigator的子节点,和modal放在一个层面
学会导航嵌套是必须的,只有通过嵌套,才能正确处理不同界面的显示问题。但是嵌套又会带来导航界面切换不清晰问题,当你在一个比较深的tab里面,怎么切换到另外一个tab的比较深的界面呢?其实你只需要直接使用navigate方法即可,因为在react-navigation内部维护了一个map关系,它保证整个navigation网络的每一个节点只有一个名字,即使你再深,我都能瞬间定位你。
2018-06-11 7862 react-native
分析的很到位,赞一个。
一般来说 tabs 都是由多个 stack 来构成,但是位于 tabA 中的 a 页面如果导航到 tabB 中的 b 页面,导航器会先跳出 tabA stack 然后再进入到 tabB stack 到具体的页面,我现在的需求是如何直接从 a 页面导航到 b 页面 就像 stack 那种,然后退出的时候也像 stack 的行为一样,对于这种需求你有遇到过吗?