Skip to content

Latest commit

 

History

History
378 lines (295 loc) · 12.3 KB

Router.md

File metadata and controls

378 lines (295 loc) · 12.3 KB

目录

路由

umi-preset-react-native提供了 2 种可相互替代的路由方案:

使用 umi 内置的 react-router

umi内置了react-router-domumi-preset-react-native使用alias在编译时将其替换为:react-router-native

二者都基于 react-router,但存在一些差异。

Link组件在 RN 和 DOM 中存在差异

以下是react-router-native Link组件的属性:

Link.propTypes = {
  onPress: PropTypes.func,
  component: PropTypes.elementType,
  replace: PropTypes.bool,
  to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
};

在 RN 中,只能这样使用Link

import React from 'react';
import { Link } from 'umi';
import { List } from '@ant-design/react-native';

const Item = List.Item;

function Index() {
  return (
    <List>
      <Link to="/home" component={Item} arrow="horizontal">
        主页
      </Link>
      <Link to="/login" component={Item} arrow="horizontal">
        登录页
      </Link>
    </List>
  );
}

没有NavLink组件

react-router-native没有NavLink组件,当你尝试引入时会得到undefined

import { NavLink } from 'umi';

typeof NavLink === 'undefined'; // true;

新增BackButtonAndroidBackButton组件

对于 RN 应用,需要在全局 layout中使用BackButton作为根容器:

// layouts/index.js
import React from 'react';
import { SafeAreaView, StatusBar } from 'react-native';
import { BackButton } from 'umi';

const Layout = ({ children }) => {
  return (
    <BackButton>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>{children}</SafeAreaView>
    </BackButton>
  );
};

export default Layout;

这样做,当用户使用Android 系统返回键时会返回应用的上一个路由,而不是退出应用。

使用 react-navigation

扩展配置

以下是安装umi-preset-react-navigation后,扩展的 umi 配置

reactNavigation

theme字段选填,下面示例中填入的是默认值,等价于不填:

// .umirc.js
export default {
  reactNavigation: {
    // 使用 ant-design 默认配色作为导航条的默认主题
    theme: {
      dark: false,
      colors: {
        primary: '#108ee9',
        background: '#ffffff',
        card: '#ffffff',
        text: '#000000',
        border: '#dddddd',
      },
    },
  },
};

扩展运行时配置

查看 umi 文档,了解什么是:运行时配置

以下是安装umi-preset-react-navigation后,扩展的运行时配置

getReactNavigationInitialState

异步(async)函数,返回的 promise resolve 后的结果会传给 react-navigation 作为初始状态。

返回类型:Promise<object | void | undefined>

getReactNavigationInitialIndicator

自定义初始化 react-navigation 状态过程中的指示器/Loading。通常在实现了上面的getReactNavigationInitialState后才会生效。

缺省情况下会使用一个内置的简陋 Loading。

onReactNavigationStateChange

异步(async)函数,用于订阅 react-navigation 状态变更通知,在每次路由变动时,接收最新状态。

getReactNavigationDefaultScreenOptions

为所有路由设置屏幕属性

案例:自定义页面转场动画
import { TransitionPresets } from 'umi';

export function getReactNavigationDefaultScreenOptions() {
  /**
   * 查看 screenOptions 全字段:https://reactnavigation.org/docs/stack-navigator/#options
   *
   * 页面转场动画相关设置,可选值:
   * - ScaleFromCenterAndroid: Standard Android navigation transition when opening or closing an Activity on Android 10 (Q).
   * - RevealFromBottomAndroid: Standard Android navigation transition when opening or closing an Activity on Android 9 (Pie).
   * - FadeFromBottomAndroid: Standard Android navigation transition when opening or closing an Activity on Android < 9 (Oreo).
   * - SlideFromRightIOS: Standard iOS navigation transition
   * - ModalSlideFromBottomIOS: Standard iOS navigation transition for modals.
   * - ModalPresentationIOS: Standard iOS modal presentation style (introduced in iOS 13).
   * 根据当前平台(iOS/Android)自动探测:
   * - DefaultTransition: Default navigation transition for the current platform.
   * - ModalTransition: Default modal transition for the current platform.
   */

  // 统一 iOS/Android 页面动画为从右侧滑入
  return {
    ...TransitionPresets.SlideFromRightIOS,
  };

  // 也可以返回一个 thunk 函数
  // return ({ route }) => {
  //   console.info('screenOptions:', route);
  //   // 可以单独为某个路由设置:
  //   if (route.name === '/login') {
  //     // 比如为 /pages/login.js 页面设置为从底部滑入
  //     return {
  //       ...TransitionPresets.ModalSlideFromBottomIOS,
  //     };
  //   }
  //
  //   return { ...TransitionPresets.SlideFromRightIOS };
  // };
}
案例:持久化导航状态

RN 工程根目录下app.js文件:

// app.js
import { Linking, Platform, Text } from 'react-native';
/**
 * AsyncStorage 将来会从 react-native 库中移除。
 * 按照 RN 官方文档引用:https://github.com/react-native-community/async-storage
 */
import AsyncStorage from '@react-native-community/async-storage';

const PERSISTENCE_KEY = 'MY_NAVIGATION_STATE';

// 返回之前本地持久化保存的状态,通常用于需要复苏应用、状态恢复的场景。
export async function getReactNavigationInitialState() {
  try {
    const initialUrl = await Linking.getInitialURL();
    if (Platform.OS !== 'web' && initialUrl == null) {
      const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY);
      if (savedStateString) {
        return JSON.parse(savedStateString);
      }
    }
  } catch (ignored) {}
}

// 自定义返回初始状态过程中显示的Loading,只有实现了 getReactNavigationInitialState 才会生效。
export function getReactNavigationInitialIndicator() {
  // 下面这个就是内置的简陋Loading:
  return ({ error, isLoading }) => {
    if (__DEV__) {
      if (isLoading) {
        return React.createElement(Text, null, 'Loading...');
      }
      if (error) {
        return React.createElement(
          View,
          null,
          React.createElement(Text, null, error.message),
          React.createElement(Text, null, error.stack),
        );
      }
    }
    return React.createElement(Text, null, 'Loading...');
  };
}

// 订阅 react-navigation 状态变化通知,每次路由变化时,将导航状态持久化保存到手机本地。
export async function onReactNavigationStateChange({ state }) {
  if (state) {
    await AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state));
  }
}
  • 如果你需要用到@react-native-community/async-storage请按照https://github.com/react-native-community/async-storage安装;
  • 安装完成后,记得进到 ios 目录使用 pod 安装原生依赖:cd ios && pod install && cd -,之后记得使用yarn iosyarn android重新编译,启动原生 App。

扩展路由属性

查看 umi 文档,了解什么是:扩展路由属性

案例:单独为某个页面设置导航条

使用扩展路由属性定制顶部导航条:

import React from 'react';
import { Text } from 'react-native';
import { Button } from '@ant-design/react-native';

function HomePage({ navigation }) {
  // 处理导航条右侧按钮点击事件
  function onHeaderRightPress() {
    // do something...
  }

  // 设置导航条右侧按钮
  useLayoutEffect(() => {
    navigation.setOptions({
      headerRight: () => (
        <Button type="primary" size="small" onPress={onHeaderRightPress}>
          弹窗
        </Button>
      ),
    });
  }, [navigation]);

  return <Text>Home Page</Text>;
}

// 扩展路由属性:
HomePage.title = 'Home Page';
HomePage.headerTintColor = '#000000';
HomePage.headerTitleStyle = {
  fontWeight: 'bold',
};
HomePage.headerStyle = {
  backgroundColor: '#ffffff',
};
// headerRight 也可以写在这里:
// HomePage.headerRight = () => (
//  <Button type="primary" size="small">
//    弹窗
//  </Button>
// );

export default HomePage;

如果页面的title属性未设置,则使用.umirc.js中的全局title

页面间跳转

查看 umi 文档:页面间跳转,姿势保持不变。

使用声明式Link组件时需要注意,在 RN 中 与 DOM 存在较大差异:

import React from 'react';
import { Link } from 'umi';
import { List } from '@ant-design/react-native';

const Item = List.Item;

function Index() {
  return (
    <List>
      <Link to="/home" component={Item} arrow="horizontal">
        主页
      </Link>
      <Link to="/login" component={Item} arrow="horizontal">
        登录页
      </Link>
    </List>
  );
}

使用命令式跳转页面时,只能使用history的 API,umi-preset-react-navigation目前还不支持使用react-navigation提供的navigation来跳转,只能做导航条设置之类的操作。

页面间传递/接收参数

IndexPage点击Link,携带query参数路由到HomePage:

import React from 'react';
import { Link } from 'umi';
import { List } from '@ant-design/react-native';

const Item = List.Item;

export default function IndexPage() {
  return (
    <List>
      <Link to="/home?name=bar" component={Item} arrow="horizontal">
        主页
      </Link>
    </List>
  );
}
export default function HomePage({ route }) {
  console.log(route); // route 属性字段查看下面

  // ...
}

route属性示例:

{ "key": "/home-WnnfQomYXFls0kS0v0lxo", "name": "/home", "params": { "name": "bar" } }

了解详情,请移步至:umi-preset-react-navigation