如何用RN建设一座大厦(RN Navigation讲解)

想象一下,你是一名房企老板,你已经得到审批可以建设你自己的大厦了,工程人员和设计人员他们决定使用React-Native这种方式来建设这座大厦。

而你,作为一个懂技术懂设计的老板,你让你雇佣的设计师和工程师们给你介绍一下,他们准备如何使用React-Native这种方式建设大厦,大厦的结构是怎么样的,楼层之间是如何联通的等等。

这次,负责给各位老板讲解的建筑工程师,就是我。

声明

首先声明,我们使用React-Native的主要目的还是在iOS和Android的地基上盖大厦,web我们暂且不考虑。

大厦入口

每座优秀的大楼,肯定都有自己不同凡响的门面,那这次这个项目呢,我们使用React-Native方式来设计打造我们的入口。

首先,在package.json中,我们可以配置入口文件。如下面的配置显示,我们选择expo/AppEntry.js 中提供的默认方式。

{  "main": "node_modules/expo/AppEntry.js",}

在它之中,它默认去注册了这个文件../../App 中的控件。

import registerRootComponent from 'expo/build/launch/registerRootComponent';
import App from '../../App';

registerRootComponent(App);

所以,真正的大门,我们要App中实现。

然而,各位老板如果有别的需求,我们也可以不在package.json中配置,而在根目录下面建立index入口文件,注册自己想要的控件。

import { AppRegistry, LogBox } from 'react-native';
LogBox.ignoreAllLogs(true);
AppRegistry.registerComponent('BossGate', () => require('./BossApp').default);
AppRegistry.registerComponent('EmployeGate', () => require('./EmployeApp').default);

当然,想保持神秘感的老板们可能并不想和自己的员工们使用同一个入口,应对这种需求,我们打算除了员工入口,给各位老板们单独开辟一个大门,更气派,更私密,更符合你的身份。

大厦基础结构

想象一下,你已经通过你自己私密恢弘的大门,进入到了大厦内部了。

接下来我要为各位老板介绍一下我们整个大厦的基础结构。

这个大厦基础结构,我们使用一个业界通用方案来解决,各位老板请放心,它非常可靠和稳定。

github地址: react-navigation

接下来我们一层层进行介绍。

const App = () => (
  <NavigationContainer>
    <Stack.Navigator>
      <Stack.Screen name="HomeScreen"
                    component={HomeScreen}/>
      <Stack.Screen name="DetailsScreen"
                    component={DetailsScreen}
                    options={{ headerShown: false }} />
      <Stack.Group screenOptions={{ presentation: 'modal' }}>
          <Stack.Screen name="WebViewPage"
                        component={WebViewPage}
                        options={{ statusBarStyle: STATUS_BAR_STYLE }}/>
      </Stack.Group>
    </Stack.Navigator>
  </NavigationContainer>
);

NavigationContainer

这个是我们大厦的最外层结构,就像鸡蛋壳一样包裹着大厦内部所有组件。

有了它,我们可以管理我们的大厦,也就是APP中导航的各个状态,并且可以给内部各个组件提供导航的上下文。

function NavigationContainerInner(
  {
    theme = DefaultTheme,
    linking,
    fallback = null,
    documentTitle,
    onReady,
    ...rest
  }: Props<ParamListBase>,
  ref?: React.Ref<NavigationContainerRef<ParamListBase> | null>
)

从它的方法定义中可以看出,NavigationContainer 作为最外部的壳,可以处理主题,路由配置,web页面的标题以及当前整个大厦的状态等。

例如,现在我们的大厦是一个默认主题,比较清冷,各位老板可以根据自己的喜好调整我们大厦的主题色,文本颜色等等,就算是猛男粉也行。

并且我们可以在大厅就给大厦放一个整个大厦的地图linking, 将来用来指导员工,如何从一个房间/楼层到另一个房间/楼层。

Navigator

再次进入大厦内部,我们会有各个楼层,有的老板喜欢打球,弄个一层的只用来打篮球,有的老板喜欢唱歌,那我们就把这一层都整成KTV。

各位老板浸淫建筑行业这么多年,相信大家也都了解,我们业界有一些基本的楼层组织方式,主要有以下几种:

→ Stack(createStackNavigator)

相信各位老板对于这种方式最熟悉。

image.png

页面和页面之间使用栈的方式组织起来,从一个页面跳到下一个页面,然后可以一层一层返回到上一个页面。

想象一下,你作为一个财大气粗的老板,想不想拥有一个走也走不到尽头的大套间???

唯一的缺点就是,入口只有一个,你的卧室要是在最里面,早上想出门就只能再穿过一个一个房间出去。

不过这不妨碍你的内心就像这个栈一样深邃。

在实际施工当中,我们可能不会用到createStackNavigator, 而是使用createNativeStackNavigator

createStackNavigator 使用的是 React Navigation 提供的默认堆栈导航器组件。它基于 JavaScript 实现,适用于多平台,包括 iOS、Android 和 Web。它提供了丰富的配置选项和功能,可以满足大多数应用的需求。但是缺点是在某些特定场景下性能稍低于原生导航器,我相信各位老板不想在穿越房间的时候有卡顿或者不够丝滑吧。

createNativeStackNavigator 是针对原生平台的堆栈导航器组件。它基于原生导航器实现,保留原生的一些交互方面,性能也更加优秀。让你丝滑的穿越各个房间,还有漂亮的原生的过渡动画。

但是也有一丝丝缺点,就是定制化稍微差了点,并且因为需要根据不同地基,单独进行一些配置。

→ Tab-bar(createBottomTabNavigator)

话说回来,各位老板如此优秀,就算是如此深邃的心灵,你可能也想七窍玲珑,你说你想要七个这样的大套间?没问题,安排。 使用Tabbar,或者叫做BottomBar,我们给你建了一条走廊,走廊上并排把你的套间们都排列开,让你随意进入任何一个。

image.png

→ Drawer(createDrawerNavigator)

当然,我们也知道,整整一层的超大套件,怎么可能就轻易满足各位老板的野心呢? 我当然知道你需要一部电梯,但是电梯还需要花费时间到达每一层,不够酷炫,我们直接上虫洞!让你轻松瞬间到达每一层! 为了掩饰我们已经达到了物理前沿,我们只能将这种技术起一个接地气的名字 —- 抽屉。

image.png

→ Top-bar(createMaterialTopTabNavigator)

看样子我们已经解决了大部分酷炫的出行需求,可是我们忘了一点,在套间内部呢??? 我们难道真的会让老板们一层层从套件中走出来吗?

image.png

当然不,我们决定,将套间中的房间进行功能区拆分,在一个大房间里,你就可以拥有客厅,卧室,洗手间,满足你的一切需求。 所以,我们使用Top-bar,或者也叫Page-view, 让你直接拥有一整个独立的功能区,并且利用这个通道,你能够快速到达你想去的房间。无论是滑(划动)过去,还是穿越(点)过去。

→ 配置

export declare type DefaultNavigatorOptions<ParamList extends ParamListBase, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase> = DefaultRouterOptions<Keyof<ParamList>> & {
    /**
     * Optional ID for the navigator. Can be used with `navigation.getParent(id)` to refer to a parent.
     */
    id?: string;
    /**
     * Children React Elements to extract the route configuration from.
     * Only `Screen`, `Group` and `React.Fragment` are supported as children.
     */
    children: React.ReactNode;
    /**
     * Event listeners for all the screens in the navigator.
     */
    screenListeners?: ScreenListeners<State, EventMap> | ((props: {
        route: RouteProp<ParamList>;
        navigation: any;
    }) => ScreenListeners<State, EventMap>);
    /**
     * Default options for all screens under this navigator.
     */
    screenOptions?: ScreenOptions | ((props: {
        route: RouteProp<ParamList>;
        navigation: any;
    }) => ScreenOptions);
};

这一层中有很多房间,我们可以用initialRouteName设置楼层的初始房间是哪个;

也可以用screenOptions 来给这一层所有的房间,统一改变一下样式,例如:

1. title:设置屏幕的标题。
2. headerShown:控制是否显示导航栏。设置为false将隐藏导航栏。
3. headerStyle:设置导航栏的样式,如背景色、阴影等。
4. headerTitleStyle:设置导航栏标题的样式,如字体大小、颜色等。
5. headerTintColor:设置导航栏标题和图标的颜色。
6. headerTitleAlign:设置导航栏标题的对齐方式。
7. headerLeft和headerRight:自定义导航栏左侧和右侧的组件。
8. headerBackTitle和headerBackTitleVisible:设置返回按钮的标题和可见性。
9. headerBackImage:自定义返回按钮的图标。
10. cardStyle:设置屏幕的过渡效果和样式。
11. gestureEnabled:控制手势返回是否可用。
12. tabBarVisible:在标签导航器中,控制底部标签栏的可见性。
13. tabBarIcon:设置标签栏图标的显示。
等等

或者用screenListeners来在走廊中放置一些监控设备,来看看你的员工们有没有进入房间,是否有破坏房间。

Screen

各位老板现在已经知道我们大概会有哪些类型的楼层了,那么我们接下来介绍介绍房间里面。

每一个房间都是一个Screen,此处各位老板可以发挥自己的想象力,怎样的房间都行,无论是奥特曼主题还是兵马俑主题,无论是充满泡泡糖的房间,还是中间只有一盒巧克力的房间都行。

只需要给房间起个响亮的名字,然后把设计图交给我,再加点你的私人需求,一切皆可满足。

<Stack.Screen name="Root" component={Root} options={{ headerShown: false }}/>

甚至,是另一个楼层!以此,我们的大厦完成了进化,可以成为堪比重庆的4D城市!就像下面这样。

function Home() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Feed" component={Feed} />
      <Tab.Screen name="Messages" component={Messages} />
    </Tab.Navigator>
  );
}

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="Home"
          component={Home}
          options={{ headerShown: false }}
        />
        <Stack.Screen name="Profile" component={Profile} />
        <Stack.Screen name="Settings" component={Settings} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Group

老板们雇员一定很多,部门也很多,那这么多房间,我们当然也有办法将他们分门别类一下进行管理。

万一哪天老板大笔一挥,给技术部门分配上十个办公室,两个专门用来电竞!

此时,我们可以使用Group, 然后把这十个办公室不小心都变成了电竞室。😝

<Stack.Group screenOptions={{ presentation: 'modal' }}>
    <Stack.Screen name="Help" component={Help} />
    <Stack.Screen name="Invite" component={Invite} />
</Stack.Group>

大厦内部出行方式

作为一名优秀的设计师,为了让老板们过的舒适,怎么能让老板自己走路呢?

首先,相信大家目前都能理解,我们整个大厦的内部结构,会使用各种navigatorscreen进行不断地嵌套,进行构建。

并且每个navigatorscreen 都会有自己的名称,所以一旦老板们进入大厦,乘坐我们的Navigation,我们就能让你去到任何你想去的房间或者楼层。

那就是路由机制!Route

有了地址后,那我们的出行方式会有哪些呢?

Navigate

navigation.navigate(name, params)

这里,我们再次用上前沿科技!我们可以直接使用房间名称,从一个房间穿梭到另一个房间或者另一个楼层。

虽然,中间穿梭的过程只在一瞬间,但是实际上,在iOS和Android内部,系统帮我们做了很多页面生命周期的管理工作。

并且,相比于iOS或者Android原生都会更加方便。

比如,有一个经典的场景是这样,我们从帖子列表进入了帖子详情页正在看帖子,但是突然来了消息,此时,点击消息我们需要进入到消息聊天页面,但是退出的时候,需求是回到消息列表页。帖子列表位于第一个Tab, 消息列表位于第二个Tab。

如果是iOS传统开发,我们需要做的是,先从帖子详情退出到帖子列表,然后切换Tab到第二个,即消息列表,然后再进入到消息详情页。

但是在RN中,我们无需这么麻烦,只要一开始定义好这些页面的嵌套关系,再调用navigate 方法,系统会帮助我们处理这复杂的中间关系,页面之间的关系会自动遵循我们在定义中写的那样。

Modal

另外,我们也可以设置一下这个房间或者楼层的进入方式是Modal方式。

此方式也是一枚黑科技 — — 多次元!

想象一下,在这个房间或者楼层之上直接开辟另一个次元,进入其中,退出的时候,可以直接退出这整个次元,回到之前次元的房间中。

<NavigationContainer>
    <StatusBar style="auto" />
        <RootStack.Navigator initialRouteName="TabBar" screenOptions={{headerShown: false}}>
                <RootStack.Screen
                                name="TabBar"
                                component={TabBar}/>
        <RootStack.Screen
                name="Post"
                component={PostStack}
                options={{presentation: 'fullScreenModal'}}/>
     </RootStack.Navigator>
</NavigationContainer>
image.png

Replace — — Stack

navigation.replace(name, params)

设想一下,你现在大套间中,但是你正身处于其中的一个房间,他是一个很low的标间,不符合你的身份,你想换一个总统套房,而你作为老板,我们不会让你挪动一丝一毫的,我们让宇宙为你而变!

我们会直接干掉这个标间,用一个总统套房替换它!你可能会永远失去这个标间,但是这个房间已经为你变成总统套房啦!

replace 方法用于替换当前页面,并打开一个新的页面。使用 replace 方法后,当前页面将被销毁,同时新的页面将成为导航堆栈中的最新页面。

Reset — — Stack

navigation.reset(state)

replace相似,你可能根本连这一整个无限套间都不喜欢,没关系,我们直接使用reset 方法,清空这整个套间中的所有房间,然后把他变成一个超级大平层总统套房。

reset 方法用于重置导航堆栈。使用 reset 方法后,我们可以将整个堆栈的状态完全重置为新的状态。

Push — — Stack

navigation.push(name, params)

push 方法和navigate 方法类似,但是navigate 方法会自动管理之前的路由状态,如果要去的页面已经在堆栈中,他会直接返回到那个页面,而push 会忽略,所以使用push方法可以使同一个页面进入多次,每次都生成一个新的,然后放在堆栈的最上层。

Pop — — Stack

navigation.pop(count)

pop方法 帮助我们返回到堆栈中的上一个页面,我们可以自定义返回到第几个页面。

PopToTop — — Stack

navigation.popToTop()

popToTop方法会直接让我们返回堆栈的第一个页面,即带我们回到套间的第一个房间中。

JumpTo — — BottomBar/TopBar/Drawer

navigation.jumpTo(name, params)

jumpTo方法 可以帮助我们在BottomBar/TopBar/Drawer这几种楼层中,直接到达任何一个在楼层下面的房间。

goBack

navigation.goBack()

我们进入房间时,都会留有历史记录,而goBack方法会帮助我们直接放回历史记录中的上一个房间或楼层,非常方便。

Deep linking

介绍到这里,相信各位老板已经胸有成竹,已经了解自己大厦中的各种特性了。

但是,我也知道,老板们交友广泛,难免会有些朋友要来造访,没有一份地图给这些尊贵的客人作为导航怎么行?

所以,为了应对这种情况,我们在大厅中,也会给外来访客放置一份地图,各位老板可以自己决定在地图上标识哪些房间和楼层,除此以外的房间当然只对内部开放喽。

这个地图,我们叫做Deep linking!

首先我们的大厦得在这城市当中有个名字,让客人方便找到:

const linking = {
  prefixes: ['mychat://', 'https://mychat.com', 'https://*.mychat.com'],
};

加入我们现在想让客人去到W06会议室。我们可以如下设置:

const config = {
  screens: {
    Home: {
      screens: {
        Room: 'rooms/:name',
      },
    },
    Profile: 'user',
  },
};

我们把这份地图放到大厅当中,客人就可以通过mychat://rooms?name=W06 直接进入到W06会议室啦。

import { NavigationContainer } from '@react-navigation/native';

const config = {
  screens: {
    Home: {
      screens: {
        Room: 'rooms/:name',
      },
    },
    Profile: 'user',
  },
};

const linking = {
  prefixes: ['mychat://', 'https://mychat.com', 'https://*.mychat.com'],
  config,
};

function App() {
  return (
    <NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
      {/* content */}
    </NavigationContainer>
  );
}

rooms/:name会帮我们找到Home/Room这Screen,然后传递过去name=W06这个参数。

结语

对于客户端来说,导航实际上是一个非常庞杂的系统功能,React Navigation的官网对于各个模块的介绍也都非常详尽。

我希望通过一个故事场景,然后用建筑结构来进行类比,能够把这些庞杂的功能串起来,让大家更加清晰的了解,这些设计的初衷和应用场景都是怎样的。可能针对代码和API的部分不够多,但是这部分官方文档其实更加详细,这里再写就有些赘述。

当然,可能有些地方类比的不是那么合适,大家有任何不明白的地方,希望能够与我一起讨论,可能会有新的理解产生,更好地服务于用户交互和业务。

Reference: https://reactnavigation.org

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容