umi(快速上手)

学习一门框架最快的方式,就是找个简单的栗子上手了。

快速上手

环境准备

  • node v8.1以上

  • 推荐用yarn,并使用国内源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 国内源
    # 需要root权限
    $ npm i yarn tyarn -g
    # 后面文档里的 yarn 换成 tyarn
    $ tyarn -v

    # 阿里内网源
    $ tnpm i yarn @ali/yarn -g
    # 后面文档里的 yarn 换成 ayarn
    $ ayarn -v
  • 全局安装umi

    1
    2
    3
    4
    $ yarn global add umi
    # 确保版本2.00+
    $ umi -v
    2.9.6
  • 创建项目

    1
    2
    3
    4
    5
    6
    7
    # 找个地方建立空目录
    $ mkdir myapp && cd myapp
    # umi generate
    $ umi g page index
    $ umi g page users
    # 启动本地服务器
    $ umi dev

约定式路由

umi devpages 下多了个 .umi 的目录作为umi的临时目录,每次重启或者pages下的文件修改都会重新生成这个文件夹下的文件,所以不要直接在里面修改代码。

indexusers里直接加一些路由跳转逻辑

1
2
3
4
5
6
7
8
9
10
11
12
//pages/index.js
+ import Link from 'umi/link';
import styles from './index.css';

export default function() {
return (
<div className={styles.normal}>
<h1>Page index</h1>
+ <Link to="/users">go to /users</Link>
</div>
);
}
1
2
3
4
5
6
7
8
9
10
11
12
//pagess/users.js
+ import router from 'umi/router';
import styles from './index.css';

export default function() {
return (
<div className={styles.normal}>
<h1>Page index</h1>
+ <button onClick={() => { router.goBack(); }}>go back</button>
</div>
);
}

这是,indexusers两个页面可以路由跳转了。

部署发布

  • 构建

    1
    2
    $ umi build
    # 构建产物默认生成到./dist下
  • 本地验证

    1
    2
    3
    # 发布之前,做本地验证
    $ yarn global add serve
    $ serve ./dist
  • 部署

    1
    2
    $ yarn global add now
    $ now ./dist

测试与配置检查

  • 测试

    1
    2
    # 内置了基于 jest 的测试工具 umi-test 
    $ umi test
  • 配置检查

    1
    $ umi inspect

脚手架创建umi项目

1
2
3
4
5
6
7
#
$ mkdir myapp && cd myapp
$ yarn create umi
# 选择project、TypeScipt、需要的功能(如dva、antd)
# 选择之后又会自动创建好目录和文件
# 启动
$ yarn start

路由 约定式路由

基础路由

1
2
3
4
5
6
7
8
9
10
11
12
13
// umi根据pages目录自动生成路由配置
+ pages/
+ users/
- index.js
- list.js
- index.js

//生成路由配置
[
{ path: '/', component: './pages/index.js' },
{ path: '/users/', component: './pages/users/index.js' },
{ path: '/users/list', component: './pages/users/list.js' },
]

动态路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//umi约定,带$前缀的目录或文件为动态路由
pages/
+ $post/
- index.js
- comments.js
+ users/
$id.js
- index.js

//生成路由配置
[
{ path: '/', component: './pages/index.js' },
{ path: '/users/:id', component: './pages/users/$id.js' },
{ path: '/:post/', component: './pages/$post/index.js' },
{ path: '/:post/comments', component: './pages/$post/comments.js' },
]

可选的动态路由

1
2
3
4
5
6
7
8
9
10
11
//umi约定带$后缀的为可选动态路由
+ pages/
+ users/
- $id$.js
- index.js

//生成路由配置
[
{ path: '/': component: './pages/index.js' },
{ path: '/users/:id?': component: './pages/users/$id$.js' },
]

嵌套路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//umi约定目录下有_layout.js时会生成嵌套路由,以_layout.js为该目录的layout
+ pages/
+ users/
- _layout.js
- $id.js
- index.js

//生成路由配置
[
{ path: '/users', component: './pages/users/_layout.js',
routes: [
{ path: '/users/', component: './pages/users/index.js' },
{ path: '/users/:id', component: './pages/users/$id.js' },
],
},
]

全局layout

1
2
3
4
5
6
7
8
9
10
11
// umi约定src/layouts/index.js 为全局路由,返回一个 React 组件,通过 props.children 渲染子组件。
// 形如:
export default function(props) {
return (
<>
<Header />
{ props.children }
<Footer />
</>
);
}

不同的全局layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// umi不支持直接配置。
// 但可以在layouts/index.js 对 location.path 做区分,渲染不同的 layout 。
export default function(props) {
if (props.location.pathname === '/login') {
return <SimpleLayout>{ props.children }</SimpleLayout>
}

return (
<>
<Header />
{ props.children }
<Footer />
</>
);
}

404路由

1
2
3
4
5
6
7
//umi约定pages/404.js为404页面,需返回React组件
//比如可以是:
export default () => {
return (
<div>I am a customized 404 page</div>
);
};

通过注释扩展路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 约定路由文件的首个注释如果包含 yaml 格式的配置,则会被用于扩展路由。
// 如下:
/**
* title: Index Page
* Routes:
* - ./src/routes/a.js
* - ./src/routes/b.js
*/

// 生成路由
[
{ path: '/', component: './index.js',
title: 'Index Page',
Routes: [ './src/routes/a.js', './src/routes/b.js' ],
},
]

配置式路由

一个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 需要配置.umirc.(ts|js) 或者 config/config.(ts|js)中的routes属性
// 只要该配置存在,就不会对src/pages目录做约定式的解析
// component 是相对于 src/pages 目录的

export default {
routes: [
{ path: '/', component: './a' },
{ path: '/list', component: './b', Routes: ['./routes/PrivateRoute.js'] },
{ path: '/users', component: './users/_layout',
routes: [
{ path: '/users/detail', component: './users/detail' },
{ path: '/users/:id', component: './users/id' }
]
},
],
};

权限路由

配置路由的Routes来实现权限路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 约定式的通过 yaml 注释添加,配置式的直接配上即可。
// 如下 umi 会用 ./routes/PrivateRoute.js 来渲染 /list
[
{ path: '/', component: './pages/index.js' },
{ path: '/list', component: './pages/list.js', Routes: ['./routes/PrivateRoute.js'] },
]

// 一个./routes/PrivateRoute.js的栗子
export default (props) => {
return (
<div>
<div>PrivateRoute (routes/PrivateRoute.js)</div>
{ props.children }
</div>
);
}

路由动效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 以react-transition-group实现路由动效为例
// 安装依赖
// $ yarn add react-transition-group
// 在 layout 组件(layouts/index.js 或者 pages 子目录下的 _layout.js)里在渲染子组件时用 TransitionGroup 和 CSSTransition 包裹一层,并以 location.pathname 为 key。
// 在global.css中定义fade样式

import withRouter from 'umi/withRouter';
import { TransitionGroup, CSSTransition } from "react-transition-group";

export default withRouter(
({ location }) =>
<TransitionGroup>
<CSSTransition key={location.pathname} classNames="fade" timeout={300}>
{ children }
</CSSTransition>
</TransitionGroup>
)

面包屑

面包屑其实就是一个告诉来访者他们目前在网站中所处的位置及如何返回的导航组件。(一般设置为3层结构)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 以 react-router-breadcrumbs-hoc为例
// 安装依赖 $ yarn add react-router-breadcrumbs-hoc

// 然后实现一个 Breakcrumbs.js
// 然后在需要的地方引入此React组件

import NavLink from 'umi/navlink';
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';

const routes = [
{ path: '/', breadcrumb: '首页' },
{ path: '/list', breadcrumb: 'List Page' },
];

export default withBreadcrumbs(routes)(({ breadcrumbs }) => (
<div>
{breadcrumbs.map((breadcrumb, index) => (
<span key={breadcrumb.key}>
<NavLink to={breadcrumb.props.match.url}>
{breadcrumb}
</NavLink>
{(index < breadcrumbs.length - 1) && <i> / </i>}
</span>
))}
</div>
));

页面间跳转

声明式

1
2
3
4
5
6
// 通常作为组件使用
import Link from 'umi/link';

export default () => (
<Link to="/list">Go to list page</Link>
);

命令式

1
2
3
4
5
6
// 通常在时间处理中被调用
import router from 'umi/router';

function goToListPage() {
router.push('/list');
}
-------------要说再见啦感谢大佬的光临~-------------