拔丝太阳 發表於 2020-1-17 20:06:00

React Navigation 学习

<div class="ct-edit-catalog" data-style="none" data-pm-slice="0 0 []">&nbsp;</div>
<blockquote>
<p>React Navigation: Android 和 iOS 设备上的路由工具,包括手势和动画。</p>
</blockquote>
<h2 id="kQIcwQF3" class="ct-heading">零、环境篇</h2>
<p>在使用 react-navigation 之前,我们需要创建一个 react-native 项目。(参考https://reactnative.cn/docs/getting-started)</p>
<p>&nbsp;</p>
<h2 id="AS3Yg2c4" class="ct-heading">一、Navigator 的种类和创建</h2>
<p>在 web 项目中的 react-router,只负责功能实现,样式是需要开发者自己去设计的。而 react-navigation 自带了几种常见的交互和样式。它共有四种常用的 Navigator:<span class="ct-status" data-pattern="fill" data-color="#1890ff"><br></span></p>
<ul>
<li>
<p><span class="cnblogs_code">Stack</span>的功能与 react-router 类似,但是每一个页面有一个标题栏。</p>










</li>
<li>
<p><span class="cnblogs_code">Switch(Switch / AnimatedSwitch)</span>&nbsp;没有样式,为鉴权场景而生。它每次只渲染一个页面,不处理返回操作,并在你切换时将路由重置为默认状态。</p>










</li>
<li>
<p><span class="cnblogs_code">Drawer</span>菜单被放在一个抽屉中,通过一个在屏幕最左边的右滑手势,来打开抽屉。</p>










</li>
<li>
<p><span class="cnblogs_code">Tab(BottomTab / MaterialBottomTab / MaterialTopTab)</span>&nbsp;菜单被放在 Tabs 中,可以在屏幕的顶部或底部。&nbsp;</p>




</li>




</ul>
<h4 id="VMaA75Gz" class="ct-heading">1. 认识 create***Navigator</h4>
<p>创建这些导航的语法都是类似的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import { createAppContainer, createSwitchNavigator } from 'react-navigation'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import { createDrawerNavigator } from 'react-navigation-drawer'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> import { createStackNavigator }from 'react-navigation-stack'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> import { createMaterialTopTabNavigator } from 'react-navigation-tabs'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> const navigator = create***<span style="color: rgba(0, 0, 0, 1)">Navigator(
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> routes</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>       Home: { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果没有 navigation 等其他选项,也可以简写为:Home: Home</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>         screen: Home <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 加载的组件</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>         navigationOptions: {}, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> screen 配置</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>         path: 'people/:name', <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> deep-link 或者 web应用 场景下使用</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">    },
</span><span style="color: rgba(0, 128, 128, 1)">15</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> configs</span>
<span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)">17</span>       initialRouteName: '', <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始路由</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>         navigationOptions: {}, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> navigator 的配置</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>       defaultNavigationOptions: {}, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> screens 的配置</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>       paths: {} <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> deep-link 场景</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">24</span> export <span style="color: rgba(0, 0, 255, 1)">default</span> createAppContainer(navigator)</pre>
</div>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="9IbBn1ez" class="ct-heading"><strong>2. 认识 navigationOptions</strong></h4>
<p>&nbsp;<span class="cnblogs_code">navigationOptions</span>可以写在 route 中,可以写在 navigator 中(3.x 开始叫<span class="cnblogs_code">defaultNavigationOptions</span>),也可以写在 screen 中。优先级是 route &gt; screen &gt; navigator。</p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> ({ navigation, screenProps, navigationOptions }) =&gt; ({ <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> object | function</span>
<span style="color: rgba(0, 128, 128, 1)">2</span>       title: '标题', <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ⚠️默认情况下按照平台惯例设置,所以在 iOS 上标题居中,在 Android 上左对齐</span>
<span style="color: rgba(0, 128, 128, 1)">3</span>         headerTitle: &lt;Title /&gt;, //<span style="color: rgba(0, 0, 0, 1)"> 也可以设置一个组件,它可以通过 nativation.getParam、setParams 和页面通信,也可以使用 redux 等
</span><span style="color: rgba(0, 128, 128, 1)">4</span>         headerRight: &lt;Title /&gt;,
<span style="color: rgba(0, 128, 128, 1)">5</span>         headerLeft: &lt;Title /&gt;, //<span style="color: rgba(0, 0, 0, 1)"> 会覆盖返回按钮
</span><span style="color: rgba(0, 128, 128, 1)">6</span>         headerStyle: {}, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 整个标题栏</span>
<span style="color: rgba(0, 128, 128, 1)">7</span>         headerTintColor: '', <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 标题和返回按钮的颜色</span>
<span style="color: rgba(0, 128, 128, 1)">8</span>         headerTitleStyle: {}, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 标题的样式</span>
<span style="color: rgba(0, 128, 128, 1)">9</span>   })</pre>
</div>
<p>&nbsp;<img src="https://img2018.cnblogs.com/common/1124590/202001/1124590-20200117204151300-282657921.png"><img src="https://img2018.cnblogs.com/common/1124590/202001/1124590-20200117204216733-1838931978.png"></p>
</div>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="WGsy7gDa" class="ct-heading"><strong>3. 认识 createAppContainer</strong></h4>
<p><span class="cnblogs_code">createAppContainer</span>将导航配置转变成 React 组件,这时它就可以放在项目的任何地方了。生成的组件可以接受两个属性:<span class="cnblogs_code">onNavigationStateChange</span>和<span class="cnblogs_code">uriPrefix</span></p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> const AppContainer =<span style="color: rgba(0, 0, 0, 1)"> createAppContainer(navigator);
</span><span style="color: rgba(0, 128, 128, 1)">2</span>
<span style="color: rgba(0, 128, 128, 1)">3</span> &lt;<span style="color: rgba(0, 0, 0, 1)">AppContainer
</span><span style="color: rgba(0, 128, 128, 1)">4</span>   onNavigationStateChange={(prevState, newState, action) =&gt; {}} <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 监听所有的路由状态变化</span>
<span style="color: rgba(0, 128, 128, 1)">5</span>   uriPrefix="/app" <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> deep-link 场景</span>
<span style="color: rgba(0, 128, 128, 1)">6</span> /&gt;</pre>
</div>
</div>
<h2 class="ct-heading">&nbsp;</h2>
<h2 id="A508iay3" class="ct-heading">二、Navigation Prop 基础功能</h2>
<h4 id="CwDXh9oW" class="ct-heading">1. 通用导航API</h4>
<ul>
<li>
<p><span class="ct-status" data-pattern="fill" data-color="#1890ff"><span class="cnblogs_code">navigate</span><br>下图说明 stackNavigator 中的 navigate 行为。当栈内没有找到该路由对应的页面时,就推入一个新的页面,否则只是弹出到已有页面。<br>Drawer、Tab 中,一个路由只能有一个组件存在——底层也是 stack 实现,但&nbsp;<span class="cnblogs_code"><span style="color: rgba(0, 0, 255, 1)">this</span>.props.navigation.state</span>&nbsp;永远都是所有路由的集合。<br><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204238423-341422492.png">&nbsp;</span></p>











</li>
<li>
<p><span class="ct-status" data-pattern="fill" data-color="#1890ff"><span class="cnblogs_code">goBack</span><br>此图说明 stackNavigator 中的 goBack 行为,传入参数表示「以我为参考进行回退」<br>Drawer、Tab 中,goBack 默认返回初始路由。<br><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204307708-1489322254.png"></span></p>






</li>






</ul>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="HABvVj2J" class="ct-heading">2. stack 专用导航API</h4>
<ul>
<li>
<p><span class="cnblogs_code">push</span>,推入页面(和 navigate 的区别是,push不会去查找栈中是否已经有该路由)</p>











</li>
<li>
<p><span class="cnblogs_code">pop</span>,弹出页面</p>











</li>
<li>
<p><span class="cnblogs_code">popToTop</span>,弹出到底部路页面</p>











</li>
<li>
<p><span class="cnblogs_code">replace</span>,替换</p>











</li>
<li>
<p><span class="cnblogs_code">reset</span>,重置当前 navigator</p>











</li>
<li>
<p><span class="cnblogs_code">dismiss</span>,退出当前 navigator,返回上层 navigator</p>











</li>











</ul>
<p><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204344061-2004973471.png"><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204357381-1677700349.png"><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204657106-1674211332.png"></p>
<p><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204438658-1793150423.png">&nbsp;&nbsp;<img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204446775-715638795.png"><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204416001-891278322.png"></p>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="lVejLCU2" class="ct-heading">3. drawer 专用导航API</h4>
<ul>
<li>
<p><span class="cnblogs_code">openDrawer</span></p>











</li>
<li>
<p><span class="cnblogs_code">closeDrawer</span></p>











</li>
<li>
<p><span class="cnblogs_code">toggleDrawer</span>控制菜单显隐</p>




</li>




</ul>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="BM4jffPX" class="ct-heading">4. 其他通用的属性</h4>
<ul>
<li>
<p><span class="cnblogs_code">state</span></p>











</li>











</ul>
<ul>
<li>
<p><span class="cnblogs_code">setParams(name, value)</span></p>











</li>











</ul>
<ul>
<li>
<p><span class="cnblogs_code">getParams(name, defaultValue)</span></p>











</li>











</ul>
<ul>
<li>
<p><span class="cnblogs_code">isfocused() </span>&nbsp;// 是否被聚焦</p>











</li>











</ul>
<ul>
<li>
<p><span class="cnblogs_code">dangerouslyGetParent()</span>&nbsp;&nbsp;// 获取父导航 </p>











</li>











</ul>
<ul>
<li>
<p><span class="cnblogs_code">dispatch()</span>&nbsp;&nbsp;// 用 props.navigation.dispatch(action) 的方式去改变路由,如下图</p>











</li>











</ul>
<ul>
<li>
<p><span class="cnblogs_code">addListener(eventName, ({ action, context, lastState, state, type }) =&gt; {})</span>&nbsp;</p>




</li>




</ul>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="uVC1dfXc" class="ct-heading">5. 路由变化时组件生命周期</h4>
<div class="ct-note hidden-title" data-type="info">
<div class="ct-note-content">
<p>Stack 在路由出栈的时候,组件会被卸载。但是 Drawer、Tab 的组件不会被卸载,状态会一直保存。</p>











</div>











</div>
<p>&nbsp;</p>
<h2 id="4pLCQzzs" class="ct-heading">三、不传属性系列</h2>
<p>上面的这些属性都是在 screen 组件中,通过<span class="cnblogs_code"><span style="color: rgba(0, 0, 255, 1)">this</span>.props.navigation</span>调用的。这就意味着,如果有深层次的子组件想操作路由,screen 就需要将<span class="cnblogs_code">navigation</span>作为子组件的属性传递下去。以下提供了一些不传属性也能操作路由的方法:</p>
<h4 id="3MzNsREZ" class="ct-heading">1. withNavigation </h4>
<p>这是一个高阶组件,对内传递给子组件<span class="cnblogs_code">navigation</span>属性,对外暴露<span class="cnblogs_code">onRef</span>属性传递出子组件的引用</p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import React from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import { Button } from 'react-native'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> import { withNavigation } from 'react-navigation'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">class MyBackButton extends React.Component {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">render() {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>       &lt;<span style="color: rgba(0, 0, 0, 1)">Button
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>         title="Back"
<span style="color: rgba(0, 128, 128, 1)">10</span>         onPress={() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">11</span>         <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.navigation.goBack();
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">      }}
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       /&gt;
<span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">    );
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">17</span> export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> withNavigation(MyBackButton);
</span><span style="color: rgba(0, 128, 128, 1)">18</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> &lt;MyBackButton onRef={elem =&gt; (<span style="color: rgba(0, 0, 255, 1)">this</span>.backButton = elem)} /&gt;;</pre>
</div>
</div>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="CaETRsJZ" class="ct-heading">2. withNavigationFocus</h4>
<p>也是一个高阶组件,对内传递给子组件<span class="cnblogs_code">isFocused</span>属性。注意⚠️,由于是属性传递,会导致组件重新渲染,需要<span class="cnblogs_code">shouldComponentUpdate</span>来控制组件渲染次数。</p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import React from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import { Text } from 'react-native'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> import { withNavigationFocus } from 'react-navigation'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">class FocusStateLabel extends React.Component {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">render() {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> &lt;Text&gt;{<span style="color: rgba(0, 0, 255, 1)">this</span>.props.isFocused ? 'Focused' : 'Not focused'}&lt;/Text&gt;;
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">10</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> export <span style="color: rgba(0, 0, 255, 1)">default</span> withNavigationFocus(FocusStateLabel);</pre>
</div>
</div>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="FOIdWZuJ" class="ct-heading">3. 全局变量</h4>
<p>还有一种办法就是将某个 navigator 保存为全局变量,这样不同层级的页面也可以方便地互相导航。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> lib.js</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> import { NavigationActions } from 'react-navigation'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">let _root;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> const setTopLevelNavigator = (navigatorRef) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   _root =<span style="color: rgba(0, 0, 0, 1)"> navigatorRef;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> const getTopLevelNavigator = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> _root
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">11</span> export <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">setTopLevelNavigator,
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">getTopLevelNavigator
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 128, 128, 1)">15</span>
<span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> app.js</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> const App = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">18</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)">19</span>   &lt;RootNavigator ref={navigation.setTopLevelNavigator} /&gt;
<span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 128, 128, 1)">22</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> page.js</span>
<span style="color: rgba(0, 128, 128, 1)">24</span> const navigator =<span style="color: rgba(0, 0, 0, 1)"> navigation.getTopLevelNavigator();
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">navigator.dispatch(NavigationActions.navigate({
</span><span style="color: rgba(0, 128, 128, 1)">26</span>   routeName: 'Drawer'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">    action: DrawerActions.openDrawer()
</span><span style="color: rgba(0, 128, 128, 1)">28</span> }))</pre>
</div>
<p>&nbsp;</p>
<h2 id="L6CTUuQG" class="ct-heading">四、滴滴打车路由设计&nbsp;</h2>
<p>&nbsp;<img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204936197-1523942017.jpg">&nbsp; &nbsp;<img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117204951416-1546702336.jpg">&nbsp; &nbsp;<img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117205048481-1815555895.jpg">&nbsp; &nbsp;&nbsp;<img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117205112220-1601027698.jpg">&nbsp;</p>
<ul>
<li>
<p>首先,我们有一个广告页、登录页、主页的选择的场景,这三个页面是互斥的,只会存在一个,这种场景就适合用 SwitchNavigator。</p>
</li>
<li>
<p>顺风车、出租车明显是 TopTabNavigator 的交互——注意它们的上方还有一个类似标题栏的东西,这意味着可以在外面再套一层 StackNavigator(订单页也是如此)。</p>
</li>
<li>
<p>而这一层 StackNavigator 和订单页的 StackNavigator 都是属于 DrawerNavigator 的内容,于是我们就有了下图这样一个路由的结构。</p>
</li>
</ul>
<p><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117205155004-1955657988.png"></p>
<h2 class="ct-heading">&nbsp;</h2>
<h2 id="oJYIQtte" class="ct-heading">五、与 React Native 配合</h2>
<h4 id="7LuPOJVS" class="ct-heading">1. Scrollables</h4>
<p>使用 react-native 的<span class="cnblogs_code">ScrollView/FlatList/SectionList</span>的时候,有一个非常方便的交互设计:点击手机顶部的时候可以快速滚到顶部初始位置。如果想要点击 TabNavigator 的 Tab 时,也想有这种效果怎么办?可以直接使用 react-navigation 封装过的 ScrollView/FlatList/SectionList。<span class="ct-status" data-pattern="fill" data-color="#1890ff">​</span></p>
<h4 class="ct-heading">&nbsp;</h4>
<h4 id="z8hBCNvN" class="ct-heading">2. SafeAreaView</h4>
<p>react-native 的<span class="cnblogs_code">SafeAreaView</span>大家都知道,可以让手机在 ios 的刘海屏/美人尖等异型屏上能正常显示。</p>
<p>react-navigation 提供的 SafeAreaView 则多了一个属性<span class="cnblogs_code">forceInset</span>,可以让我们更加精细地控制四边的padding。它在 top | bottom | left | right | vertical | horizontal 几种方向上有两种值可以设置:'always' 和 'nerver'。<span class="ct-status" data-pattern="fill" data-color="#1890ff"><br></span></p>
<p>这里要注意的是,如果 SafeAreaView是包裹在页面上的,不包括导航栏的高度,如下图左红色部分。如果 SafeAreaView 是包裹在 RootNavigator 上的,就包括导航栏的高度,如下图右蓝色部分。当然就算我们只放在页面上,导航栏的高度也对异性屏做了兼容,使得我们的页面在ios各种机型上正常显示(react-navigation 4.x)。</p>
<p><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117205223904-1739279345.png"><img src="https://img2018.cnblogs.com/i-beta/1124590/202001/1124590-20200117205256594-1241298972.png"></p>
<p>那么,Android 异型屏怎么办?借助 react-native-device-info 识别是否有 notch,然后设置 SafeAreaView 的高度</p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import { Platform } from 'react-native'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import SafeAreaView from 'react-native-safe-area-view'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> import DeviceInfo from 'react-native-device-info'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (Platform.OS === 'android' &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> DeviceInfo.hasNotch()) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">SafeAreaView
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">    .setStatusBarHeight
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Some value for status bar height + notch height </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">    ();
</span><span style="color: rgba(0, 128, 128, 1)">10</span> }</pre>
</div>
</div>
<h2 class="ct-heading">&nbsp;</h2>
<h2 id="rbLYqB5Y" class="ct-heading">六、监听路由事件</h2>
<p>NavigiationEvents 是 react-navigation 导出的一个组件,它上面有五个属性。在任何组件上都可以放置<span class="cnblogs_code">&lt;NavigiationEvents /&gt;</span><span class="ct-status" data-pattern="fill" data-color="#1890ff"><br></span></p>
<ul>
<li>
<p><span class="cnblogs_code">onWillFocus</span></p>







</li>
<li>
<p><span class="cnblogs_code">onDidFocus</span></p>







</li>
<li>
<p><span class="cnblogs_code">onWillBlur</span></p>







</li>
<li>
<p><span class="cnblogs_code">onDidBlur</span></p>







</li>
<li>
<p><span class="cnblogs_code">navigator</span>(默认当前所处上下文)</p>







</li>







</ul>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import React from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> import { View } from 'react-native'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> import { NavigationEvents } from 'react-navigation'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> const MyScreen = () =&gt;<span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   &lt;View&gt;
<span style="color: rgba(0, 128, 128, 1)"> 7</span>   &lt;<span style="color: rgba(0, 0, 0, 1)">NavigationEvents
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>       onWillFocus={payload =&gt; console.log('will focus'<span style="color: rgba(0, 0, 0, 1)">, payload)}
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>       onDidFocus={payload =&gt; console.log('did focus'<span style="color: rgba(0, 0, 0, 1)">, payload)}
</span><span style="color: rgba(0, 128, 128, 1)">10</span>       onWillBlur={payload =&gt; console.log('will blur'<span style="color: rgba(0, 0, 0, 1)">, payload)}
</span><span style="color: rgba(0, 128, 128, 1)">11</span>       onDidBlur={payload =&gt; console.log('did blur'<span style="color: rgba(0, 0, 0, 1)">, payload)}
</span><span style="color: rgba(0, 128, 128, 1)">12</span>   /&gt;
<span style="color: rgba(0, 128, 128, 1)">13</span>   {<span style="color: rgba(0, 128, 0, 1)">/*</span>
<span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 128, 0, 1)">      Your view code
</span><span style="color: rgba(0, 128, 128, 1)">15</span>   <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">16</span>   &lt;/View&gt;
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">18</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> export <span style="color: rgba(0, 0, 255, 1)">default</span> MyScreen;</pre>
</div>
</div>
<p>路由事件会自顶向下传导,父组件、子组件的事件处理函数会依次被触发:parent will focus &gt; child will focus &gt; parent didfocus &gt; child didfocus,blur 也是一样。</p>
<p>&nbsp;</p>
<h2 id="35FzNRQp" class="ct-heading">七、其他</h2>
<p><span style="color: rgba(255, 102, 0, 1)"><strong>状态保持(实验性)(需要借助 @react-native-community/async-storage 或其他存储工具)</strong></span></p>
<p>涉及到的 API:&nbsp;<span class="cnblogs_code">persistNavigationState</span>&nbsp;、&nbsp;<span class="cnblogs_code">loadNavigationState</span>&nbsp;,功能是——刷新页面后可维持之前的路由状态,即使进程被杀掉也可以恢复噢。恢复后历史堆栈仍在,可以使用 goBack、replace等操作。</p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const AppNavigator =<span style="color: rgba(0, 0, 0, 1)"> createStackNavigator({ })
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> const persistNavigationState = async (navState) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>       await AsyncStorage.setItem('myNavigator'<span style="color: rgba(0, 0, 0, 1)">, JSON.stringigy(navState))
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (e) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> const loadNavigationState = async() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   const jsonString = await AsynStorage.getItem('myNavigator'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> JSON.parse(jsonString)
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">12</span> const App = () =&gt; &lt;<span style="color: rgba(0, 0, 0, 1)">AppContainer
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   persistNavigationState=<span style="color: rgba(0, 0, 0, 1)">{persistNavigationState}
</span><span style="color: rgba(0, 128, 128, 1)">14</span>   loadNavigationState=<span style="color: rgba(0, 0, 0, 1)">{loadNavigationState}
</span><span style="color: rgba(0, 128, 128, 1)">15</span> /&gt;</pre>
</div>
</div>
<p>此功能在开发模式下特别有用。你可以使用以下方法,有选择地启用它:</p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> const AppContainer =<span style="color: rgba(0, 0, 0, 1)"> createStackNavigator({ })
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> getPersistenceFunctions () {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> __DEV__ ?<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">      persistNavigationState,
</span><span style="color: rgba(0, 128, 128, 1)">5</span> <span style="color: rgba(0, 0, 0, 1)">    loadNavigationState
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">} : undefined
</span><span style="color: rgba(0, 128, 128, 1)">7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">8</span> const App = () =&gt; &lt;AppContainer {...getPersistenceFunctions()} /&gt;</pre>
</div>
</div>
<p>由于状态是异步加载的,你可以在 AppContainer 中使用属性<span class="cnblogs_code">renderLoadingExperimental</span>渲染一个空页面</p>
<p>&nbsp;</p>
<p><strong>TypeScript 支持</strong></p>
<p>https://reactnavigation.org/docs/en/typescript.html</p>
<p>&nbsp;</p>
<p><strong>2.14.0 之前的版本使用 react-native-screens 来进行 native 侧的性能优化</strong></p>
<p>https://reactnavigation.org/docs/en/react-native-screens.html</p>
<p>&nbsp;</p>
<p><strong>自定义Android返回键行为</strong></p>
<p>默认情况下,当用户按下Android 物理返回键时,reat-navigation会返回到上一个页面,如果没有可返回的页面,则退出应用。</p>
<p>自定义行为需要使用 react-native 的<span class="cnblogs_code">BackHandler</span>这个API</p>
<div class="ct-code" data-language="JavaScript" data-theme="xq-light" data-title="代码块" data-expand="false">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> import { BackHander } from 'react-native'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">constructor() {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>._didFocusSubscription =<span style="color: rgba(0, 0, 0, 1)"> props.navigation.addListener(
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   'didFocus'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   payload =&gt;<span style="color: rgba(0, 0, 0, 1)"> BackHandler.addEventListener(
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>         'hardeareBackPress'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       <span style="color: rgba(0, 0, 255, 1)">this</span>.onBackButtonPressAndroid, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回 true 则表示我们已经处理了该事件,并且react-navigation 的事件监听器不会被调用,因此不会销毁当前页。 返回false会该方法继续执行 - react-navigation 的事件监听器将销毁当前页面。</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">    )
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">componentDidMount() {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>._willBlurSubscription = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.navigation.addListener(
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       'willBlur'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">14</span>       payload =&gt;
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">      BackHandler.removeEventListener(
</span><span style="color: rgba(0, 128, 128, 1)">16</span>         'hardwareBackPress'<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">17</span>         <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onBackButtonPressAndroid
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">      )
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">    );
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">componentWillUnmount() {
</span><span style="color: rgba(0, 128, 128, 1)">22</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>._didFocusSubscription &amp;&amp; <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._didFocusSubscription.remove();
</span><span style="color: rgba(0, 128, 128, 1)">23</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>._willBlurSubscription &amp;&amp; <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._willBlurSubscription.remove();
</span><span style="color: rgba(0, 128, 128, 1)">24</span>   }</pre>
</div>
<p>&nbsp;&nbsp;</p>
</div>
<p>参考:https://reactnavigation.org/</p><br><br>
来源:https://www.cnblogs.com/ppJuan/p/12207335.html
頁: [1]
查看完整版本: React Navigation 学习