美文网首页
如何用RN建设一座大厦(RN Navigation讲解)

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

作者: 宋奕Ekis | 来源:发表于2023-06-15 12:04 被阅读0次

    想象一下,你是一名房企老板,你已经得到审批可以建设你自己的大厦了,工程人员和设计人员他们决定使用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

    相关文章

      网友评论

          本文标题:如何用RN建设一座大厦(RN Navigation讲解)

          本文链接:https://www.haomeiwen.com/subject/dtklydtx.html