Overview
NavigationExperimental是react native的一个新的导航系统,重点是改进<Navigator/>组件.
- 单向数据流,它使用reducers 来操作最顶层的state 对像,而在<Navigator/>中,当你在子导航页中,不可能操作到app最初打开页面时的state对像,除非,一级级的通过props传递过方法名或函数名,然后在子页面中调用这些方法或者函数,来修改某个顶层的数据。
- 为了允许存在本地和基于 js的导航视图,导航的逻辑和路由,必须从视图逻辑中独立出来。
- 改进了切换时的场景动画,手势和导航栏
如果你对react native 中的三个导航感到困惑,可以查看导航比较的文章
三个导航系统的比较
Navigator 和 NavigatorIOS 对于新人来说,不知道如何区别它们。一个是基于JS的,而NavigatorIOS则是第三方的开发的只针对ios的本地组件. 而Facebook正在将Navigator 过渡到NavigationExperimental. NavigationExperimental向前兼容navigation库。
NavigationExperimental 通常称为”新的导航”,但其实它是以一种新的方法实现导航逻辑,这样允许作何的视图都可以作为导航的视图 。它包含了一个预编异的组件NavigationAnimatedView来管理场景间的动画。它内部的每一个视图都可以有自己的手势和动画。这些预编译的场景和overlay组件,看起来就会跟平台相一致(ios,android)
Navigator and NavigatorIOS两个都是有状态(即保存各个导航的序顺)的组件,允许你的APP在多个不同的场景(屏幕)之间管理你的导航。这两个导航管理了一个路由栈(route stack),这样就允许我们使用pop(),psh(),and replace()来管理状态。这类似于html5的history API. 这两者的主要区别在于NavigatorIOS是使用了iOS的 UINavigationController类,而Navigator都是基于Javascript。 Navigator适用于两个平台,而NavigatorIOS只能适用于iOS. 如果在一个APP中应用了多个导航组件(Navigator and NavigatorIOS一起使用). 那么在两者之间进行导航过渡,会变得非常困难.
NavigationExperimental
- NavigationRootContainer允许导航的各个状态(屏幕)保存在app的最顶层.
- NavigationReducers 包含了预置的reducers,用来管理导航状态之间的转换过渡。
- Reducers可以彼此之前进行组合,设置更高级的导航逻辑
- 导航逻辑可以用于任何的视图
- NavigationAnimatedView 是一个用来管理不同场景动画的组件,也可以用于Navigator和NavigatorIOS组件
- 每一个scene可以完全自定义,并且管理它自己的动画和手势
- 可以有一个Overlay/header,用于跟场景的动画同步
- NavigationCard 和NavigationHeader可以作为预编译的scenes和overlays. 然后跟NavigationAnimatedView一起使用
Navigator
- Facebook会慢慢不支持Navigator,重点会放在NavigationExperimental
- 它有自己的navigations state和API,这违返了React的单向数据流原则
- Scene animations and gestures很难自定义
- 可以用于iOS和Android
- 跟NavigatorIOS一样,只有一个简单的导航条:Navigator.NavigatorBar,和一个breadcrumbs Navigator.BreadcrumbNavigatorBar. 可以看看React Native的官方UIExplorer demo 看看如何使用它们。
- 动画不如Apple的精致,你可以使用NavigatorIOS.
- 你可以通过navigationBar属性,提供你自己的navigation bar
NavigatorIOS
- 包含一 个专有的API,不能很好的兼容其它的app
- API很小,所以限制了它对Navigator or NavigationStackView的自定义
- 开发这个组件不是React Native团队,而是属于开源的社区
- 有很多积压的bug
- 如果社区将它重构为声明性的(declarative),它将跟NavigationExperimental一起使用的很好
- 它在iOS UIKit的基础上包装的, 所以它跟其它的本地app是一样的。
- 仅支持iOS
- 包含一个默认的navigation bar. 这个navigation bar不是一个React Native view组件,它的样式只能轻微调整
NavigationExperimental 文档
Navigation State
你整个app的导航状态(state)可以被NavigationStates模式化,一个NavigationState是一个对像。
const myState = {
key: 'myPage0',myType: 'ExamplePage',myParams: {foo:'bar'},}
一个NavigationParentState 包含一组路由(routes),并且有一个index字段,表示当前的路由
const myState = {
key: 'myAppTabs',routes: [
{key: 'home'},{key: 'notifs'},{key: 'settings'},],index: 1,// points to the 'notifs' tab
}
navigation state types在NavigationStateUtils中保存,同时在NavigationStateUtils还有一些函数,通过这些函数可以改变NavigationParentState。
Containers
在NavigationExperimental中提供了一个最顶级的组件,用于维护导航的状态以及处理永久性(将导航保存到硬盘或者从硬盘中读取导航的状态数据)。
如果你使用redux 或者flux,你可以不需要NavigationContainer. 你可以使用现有的stores and providers.
NavigationRootContainer
开发者可以为根容器设置一个reducer,reducer会包含整个app的导航逻辑。我们的navigation reducers将会接受最后的导航状态,一个我们需要处理的action. 然后它为我们的app输出一个新的导航装态。为了获得初始化的state,reducers可以在调用时,不需要上一个状态或者action.
<NavigationRootContainer reducer={MyReducer} renderNavigation={(navigationState, onNavigate) => (
<Text>Currently at {navigationState.routes[navigationState.index]}</Text>
它也提供了一个针对navigation action的处理器,并且允许reducer被自定义.
NavigationContainer.create
在整个应用中,都要传递onNavigate会非常繁锁,因此我们可以提供一个更高阶的”container”组件。
<NavigationRootContainer
reducer={MyReducer}
renderNavigation={(navigationState) => <ExampleComponent />}
...
class ExampleComponent {
render() {
<Text onPress={() => { this.props.onNavigate(new ExampleAction()) }}>
This action will work,even though `onNavigate` was not directly passed in
</Text>
}
}
ExampleComponent = NavigationContainer.create(ExampleComponent);
如果onNavigation作为一个属性被传递给container,它会覆盖处理程序中包含的组件和所有的子容器.
Reducers
一个导航的reducer是一个action 处理器,它返回当前的navigation state.当调用navigation reducers,你要提供一个可选的prevIoUs state和一个字符串类型的 navigation action.
let state = MyReducer(null,{ type: 'InitialAction' });
//output
> {
key: 'Root',index: 0,routes: [
{key: 'Home'},]
}
state = MyReducer(state,{ type: 'PushPerson',name: 'Christopher' });
//output
> {
key: 'Root',index: 1,{key: 'Person0',name: 'Christopher'},]
}
Stack Reducer
常见的导航逻辑是一个’stack’(栈),这可以通过stack reducer来处理
const MyReducer = NavigationStackReducer({
// First,define the initial parent state that will be used if there was no prevIoUs state.
initialState: {
key: 'Root',]
},getPushedReducerForAction: (action) => {
if (action.type === 'PushPerson') {
// We need to push some additional state,that will be defined by this reducer:
return () => ({
key: 'Person'+(i++),name: action.name,});
}
// In this case we do not need to push,so our reducer for this action is nothing
return null;
},});
let state = MyReducer(null,{ type: 'InitAction' });
> {
key: 'Root',]
}
state = MyReducer(state,name: 'Christopher' });
> {
key: 'Root',]
}
// The back action can be used to pop:
state = MyReducer(state,NavigationRootContainer.getBackAction());
> {
key: 'Root',]
}
stack reducer中也可以包含sub-reducers,它需要你实现getReducerForState. 它会为sub-state 返回一个sub-reducer. 当前的sub-state的sub-reducer将会被使用.
Tabs Reducer
Tabs reducer允许你有多个子sub-reducers,但有一个是激活状态。对于每一个action,都会被发送给tabs reducer,它会首先使用active状态的sub-reducer. 如果reducers没有返回一个新的sub-state,则另外的reducers将会获得机会,并进行处理。如果一个不同的tab reducer处理了它,tabs reducer将返回一个新的new sub-state,并且交换active tab.
Find Reducer
Reducers的一个常见模式是组合了多个reducers,当其中一个reducer返回一个新的state时停止。Find Reducer会接受一个reducers数组,然后遍历数据中的每一个元素,直到state改变时,返回这个reducer. 如果这些reducers没有返回一个新的state,find reducer将返回默认的state.
Views
NavigationView
最简单的视图是render当前sub-state的场景(scene). 常用于tabs,因为它不需要转换。
NavigationAnimateView
NavigationAnimateView 采用声明API,它使用Animate library向scenes委派动画和手势
NavigationCard和NavigationHeader就是场景和叠加的NavigationAnimateView。这是为了看起来跟iOS或android一样。
NavigationCard
<NavigationAnimatedView navigationState={navigationState} renderScene={(props) => (
<NavigationCard key={props.navigationState.key} index={props.index} navigationState={props.navigationParentState} position={props.position} layout={props.layout}>
<MyInnerView info={props.navigationState} />
</NavigationCard>
)}
/>
NavigationHeader
<NavigationAnimatedView navigationState={navigationState} renderOverlay={(props) => (
<NavigationHeader navigationState={props.navigationParentState} position={props.position} getTitle={state => state.key}
/>
)}
renderScene={this._renderScene}
/>
NavigationCardStack
包装了NavigationAnimateView,可以为每一个Scene,render一个NavigationCard. 类似于过时的Navigator. 这是因为它内置了animations和gestures
使用:
render() { return ( <NavigationCardStack style={styles.main} renderScene={props => <MyPetView name={props.navigationState.key} species={props.navigationState.species} /> } renderOverlay={props => <NavigationHeader {...props} />} navigationState={{ key: 'MyPetStack',index: 2,routes: [ {key: 'Pluto',species: 'dog'},{key: 'Snoopy',{key: 'Garfield',species: 'cat'},] }} /> ); }