https://umijs.org/
$ cnpm install -g umi
$ yarn global add umi
$ umi -v
umi@3.2.15
$ yarn global bin
$ npm root -g
.
├── dist/ // 默认的 build 输出目录
├── mock/ // mock 文件所在目录,基于 express
├── config/
├── config.js // umi 配置,同 .umirc.js,二选一
└── src/ // 源码目录,可选
├── layouts/index.js // 全局布局
├── pages/ // 页面目录,里面的文件即路由
├── .umi/ // dev 临时目录,需添加到 .gitignore
├── .umi-production/ // build 临时目录,会自动删除
├── document.ejs // HTML 模板
├── 404.js // 404 页面
├── page1.js // 页面 1,任意命名,导出 react 组件
├── page1.test.js // 用例文件,umi test 会匹配所有 .test.js 和 .e2e.js 结尾的文件
└── page2.js // 页面 2,任意命名
├── global.css // 约定的全局样式文件,自动引入,也可以用 global.less
├── global.js // 可以在这里加入 polyfill
├── .umirc.js // umi 配置,同 config/config.js,二选一
├── .env // 环境变量
└── package.json
mkdir hs-umi
cd hs-umi
cnpm init -y
mkdir src/pages
umi g page index
pages\index.js
import React, { Component } from 'react'
import { Link } from 'umi';
import styles from './index.css';
export default class componentName extends Component {
render() {
return (
<div>
<h1 className={styles.title}>首页</h1>
<Link to="/profile">个人中心</Link>
</div>
)
}
}
// 用命令创建user页面
umi g page user
import React from 'react';
import styles from './user.css';
export default () => {
return (
<div>
<h1 className={styles.title}>Page user</h1>
</div>
);
}
umi g page profile
pages\profile.js
import React, { Component } from 'react'
import { history } from 'umi';
import styles from './profile.css';
export default class componentName extends Component {
render() {
return (
<div>
<h1 className={styles.title}>个人中心</h1>
<button onClick={()=>history.goBack()}>返回</button>
</div>
)
}
}
"scripts": {
"dev": "umi dev",
"build": "umi build"
}
npm run dev
npm run build
src/layouts/index.js
为全局路由,返回一个 React 组件,通过 props.children
渲染子组件src/layouts/index.js
import React,{Component} from 'react';
import {Link} from 'umi';
export default class Layout extends Component{
render() {
return (
<div>
<ul>
<li><Link to="/" >首页</Link></li>
<li><Link to="/user">用户管理</Link></li>
<li><Link to="/profile">个人设置</Link></li>
</ul>
<div>
{this.props.children}
</div>
</div>
)
}
}
import React,{Component,Fragment} from 'react';
import {Link} from 'umi';
export default class User extends Component{
render() {
return (
<div >
<ul>
<li><Link to="/user/list">用户列表</Link></li>
<li><Link to="/user/add">新增用户</Link></li>
</ul>
<div>
{this.props.children}
</div>
</div>
)
}
}
/pages/user/list.js
import React,{Component,Fragment} from 'react';
import {Link} from 'umi';
export default class List extends Component{
render() {
return (
<ul>
<li><Link to="/user/detail/1">张三</Link></li>
<li><Link to="/user/detail/2">李四</Link></li>
</ul>
)
}
}
pages/user/add.js
import React,{Component} from 'react';
export default class Add extends Component{
render() {
return (
<form >
<input/>
<input type="submit" />
</form>
)
}
}
[]
包裹的文件或文件夹为动态路由src\pages\user\detail[id].js
import React,{Component} from 'react';
export default class extends Component{
render() {
console.log(this.props);
return (
<table>
<thead>
<tr>
<td>字段</td>
<td>值</td>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>张三</td>
</tr>
</tbody>
</table>
)
}
}
wrappers
达成效果wrappers
目录下面import React, { Component } from 'react'
import { history } from 'umi';
import styles from './profile.css';
class Profile extends Component {
render() {
return (
个人中心
)
}
}
+Profile.wrappers = ['@/wrappers/auth']
+export default Profile;
src\wrappers\auth.js
import { Redirect } from 'umi';
export default (props) => {
const isLogin = localStorage.getItem('isLogin');
if (isLogin) {
return <div>{ props.children }</div>;
} else {
return <Redirect to={{pathname:"/login",state:{from:'/profile'}}} />;
}
}
pages/login.js
import React from 'react';
import styles from './login.css';
import { history } from 'umi';
export default (props) => {
return (
<div>
<h1 className={styles.title}>Page login</h1>
<button onClick={()=>{
localStorage.setItem('isLogin','true');
if(props.location.state&&props.location.state.from){
history.push(props.location.state.from);
}
}}>登录</button>
</div>
);
}
src\app.js
export function patchRoutes({ routes }) {
routes.unshift({
path: '/foo',
exact: true,
component: require('./Foo').default,
});
}
src\Foo.js
import React, { Component } from 'react'
export default class componentName extends Component {
render() {
return (
<div>
Foo
</div>
)
}
}
src\app.js
let extraRoutes;
export function modifyClientRenderOpts(memo) {
memo.routes.unshift(...extraRoutes);
return memo;
}
export function render(oldRender) {
fetch('/api/routes').then(res => res.json()).then((res) => {
extraRoutes = res.map(item=>{
let component = item.component;
component = require(`./components/${component}`).default;
return { ...item, component};
});
oldRender();
})
}
mock\routes.js
export default {
'GET /api/routes': [
{
path:'/foo',
component:'Foo.js'
}
]
}