路由

::: tip 提示
下文介绍的路由使用可以在 umi-examples/routesumi-examples/routes-via-config 里找到示例代码。
:::

umi 会根据 pages 目录自动生成路由配置。

约定式路由

基础路由

假设 pages 目录结构如下:

  1. + pages/
  2. + users/
  3. - index.js
  4. - list.js
  5. - index.js

那么,umi 会自动生成路由配置如下:

  1. [
  2. { path: '/', component: './pages/index.js' },
  3. { path: '/users/', component: './pages/users/index.js' },
  4. { path: '/users/list', component: './pages/users/list.js' },
  5. ]

动态路由

umi 里约定,带 $ 前缀的目录或文件为动态路由。

比如以下目录结构:

  1. + pages/
  2. + $post/
  3. - index.js
  4. - comments.js
  5. + users/
  6. $id.js
  7. - index.js

会生成路由配置如下:

  1. [
  2. { path: '/', component: './pages/index.js' },
  3. { path: '/users/:id', component: './pages/users/$id.js' },
  4. { path: '/:post/', component: './pages/$post/index.js' },
  5. { path: '/:post/comments', component: './pages/$post/comments.js' },
  6. ]

可选的动态路由

umi 里约定动态路由如果带 $ 后缀,则为可选动态路由。

比如以下结构:

  1. + pages/
  2. + users/
  3. - $id$.js
  4. - index.js

会生成路由配置如下:

  1. [
  2. { path: '/': component: './pages/index.js' },
  3. { path: '/users/:id?': component: './pages/users/$id$.js' },
  4. ]

嵌套路由

umi 里约定目录下有 _layout.js 时会生成嵌套路由,以 _layout.js 为该目录的 layout 。

比如以下目录结构:

  1. + pages/
  2. + users/
  3. - _layout.js
  4. - $id.js
  5. - index.js

会生成路由配置如下:

  1. [
  2. { path: '/users': component: './pages/users/_layout.js'
  3. routes: [
  4. { path: '/users/', component: './pages/users/index.js' },
  5. { path: '/users/:id', component: './pages/users/$id.js' },
  6. ],
  7. },
  8. ]

全局 layout

约定 src/layouts/index.js 为全局路由,返回一个 React 组件,通过 props.children 渲染子组件。

比如:

  1. export default function(props) {
  2. return (
  3. <>
  4. <Header />
  5. { props.children }
  6. <Footer />
  7. </>
  8. );
  9. }

不同的全局 layout

你可能需要针对不同路由输出不同的全局 layout,umi 不支持这样的配置,但你仍可以在 layouts/index.js 对 location.path 做区分,渲染不同的 layout 。

比如想要针对 /login 输出简单布局,

  1. export default function(props) {
  2. if (props.location.pathname === '/login') {
  3. return <SimpleLayout>{ props.children }</SimpleLayout>
  4. }
  5. return (
  6. <>
  7. <Header />
  8. { props.children }
  9. <Footer />
  10. </>
  11. );
  12. }

404 路由

约定 pages/404.js 为 404 页面,需返回 React 组件。

比如:

  1. export default () => {
  2. return (
  3. <div>I am a customized 404 page</div>
  4. );
  5. };

注意:开发模式下,umi 会添加一个默认的 404 页面来辅助开发,但你仍然可通过精确地访问 /404 来验证 404 页面。

通过注释扩展路由

约定路由文件的首个注释如果包含 yaml 格式的配置,则会被用于扩展路由。

比如:

  1. + pages/
  2. - index.js

如果 pages/index.js 里包含:

  1. /**
  2. * title: Index Page
  3. * Routes:
  4. * - ./src/routes/a.js
  5. * - ./src/routes/b.js
  6. */

则会生成路由配置:

  1. [
  2. { path: '/', component: './index.js',
  3. title: 'Index Page',
  4. Routes: [ './src/routes/a.js', './src/routes/b.js' ],
  5. },
  6. ]

配置式路由

如果你倾向于使用配置式的路由,可以配置 routes此配置项存在时则不会对 src/pages 目录做约定式的解析

比如:

  1. export default {
  2. routes: [
  3. { path: '/', component: './a' },
  4. { path: '/list', component: './b', Routes: ['./routes/PrivateRoute.js'] },
  5. { path: '/users', component: './users/_layout',
  6. routes: [
  7. { path: '/users/detail', component: './users/detail' },
  8. { path: '/users/:id', component: './users/id' }
  9. ]
  10. },
  11. ],
  12. };

注意:

  1. component 是相对于 src/pages 目录的

权限路由

umi 的权限路由是通过配置路由的 Routes 属性来实现。约定式的通过 yaml 注释添加,配置式的直接配上即可。

比如有以下配置:

  1. [
  2. { path: '/', component: './pages/index.js' },
  3. { path: '/list', component: './pages/list.js', Routes: ['./routes/PrivateRoute.js'] },
  4. ]

然后 umi 会用 ./routes/PrivateRoute.js 来渲染 /list

./routes/PrivateRoute.js 文件示例:

  1. export default (props) => {
  2. return (
  3. <div>
  4. <div>PrivateRoute (routes/PrivateRoute.js)</div>
  5. { props.children }
  6. </div>
  7. );
  8. }

路由动效

路由动效应该是有多种实现方式,这里举 react-transition-group 的例子。

先安装依赖,

  1. $ yarn add react-transition-group

在 layout 组件(layouts/index.js 或者 pages 子目录下的 _layout.js)里在渲染子组件时用 TransitionGroup 和 CSSTransition 包裹一层,并以 location.pathname 为 key,

  1. import withRouter from 'umi/withRouter';
  2. import { TransitionGroup, CSSTransition } from "react-transition-group";
  3. export default withRouter(
  4. ({ location }) =>
  5. <TransitionGroup>
  6. <CSSTransition key={location.pathname} classNames="fade" timeout={300}>
  7. { children }
  8. </CSSTransition>
  9. </TransitionGroup>
  10. )

上面用到的 fade 样式,可以在 src 下的 global.css 里定义:

  1. .fade-enter {
  2. opacity: 0;
  3. z-index: 1;
  4. }
  5. .fade-enter.fade-enter-active {
  6. opacity: 1;
  7. transition: opacity 250ms ease-in;
  8. }

面包屑

面包屑也是有多种实现方式,这里举 react-router-breadcrumbs-hoc 的例子。

先安装依赖,

  1. $ yarn add react-router-breadcrumbs-hoc

然后实现一个 Breakcrumbs.js,比如:

  1. import NavLink from 'umi/navlink';
  2. import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
  3. // 更多配置请移步 https://github.com/icd2k3/react-router-breadcrumbs-hoc
  4. const routes = [
  5. { path: '/', breadcrumb: '首页' },
  6. { path: '/list', breadcrumb: 'List Page' },
  7. ];
  8. export default withBreadcrumbs(routes)(({ breadcrumbs }) => (
  9. <div>
  10. {breadcrumbs.map((breadcrumb, index) => (
  11. <span key={breadcrumb.key}>
  12. <NavLink to={breadcrumb.props.match.url}>
  13. {breadcrumb}
  14. </NavLink>
  15. {(index < breadcrumbs.length - 1) && <i> / </i>}
  16. </span>
  17. ))}
  18. </div>
  19. ));

然后在需要的地方引入此 React 组件即可。

启用 Hash 路由

umi 默认是用的 Browser History,如果要用 Hash History,需配置:

  1. export default {
  2. history: 'hash',
  3. }

Scroll to top

在 layout 组件(layouts/index.js 或者 pages 子目录下的 _layout.js)的 componentDidUpdate 里决定是否 scroll to top,比如:

  1. import { Component } from 'react';
  2. import withRouter from 'umi/withRouter';
  3. class Layout extends Component {
  4. componentDidUpdate(prevProps) {
  5. if (this.props.location !== prevProps.location) {
  6. window.scrollTo(0, 0);
  7. }
  8. }
  9. render() {
  10. return this.props.children;
  11. }
  12. }
  13. export default withRouter(Layout);

参考