cnpm install -g umi
.
├── 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 zhufeng-umi
cd zhufeng-umi
cnpm init -y
mkdir pages
umi g page index
pages\index.js
import React, { Component } from 'react'
import Link from 'umi/link';
export default class componentName extends Component {
  render() {
    return (
      <div>
        首页
        <Link to="/profile">个人中心</Link>
      </div>
    )
  }
}
umi g page profile
pages\profile.js
import React, { Component } from 'react'
import router from 'umi/router';
export default class componentName extends Component {
  render() {
    return (
      <div>
        <button onClick={()=>router.goBack()}>返回</button>
        个人中心
      </div>
    )
  }
}
  "scripts": {
    "dev": "umi dev",
    "build": "umi build",
      "test": "umi test",
    "serve": "serve ./dist"
  }
npm run dev
npm run build
npm run serve
test\sum.test.js
let assert = require('assert');
describe('sum',()=>{
    it('1+1',()=>{
        assert(1+1==2);
    });
});
npm run test
约定 src/layouts/index.js 为全局路由,返回一个 React 组件,通过 props.children 渲染子组件。
cnpm i bootstrap@3 -S
src/layouts/index.js
import React,{Component,Fragment} from 'react';
import 'bootstrap/dist/css/bootstrap.css';
import Link from 'umi/link';
export default class Layout extends Component{
  render() {
      return (
          <Fragment>
              <nav className="navbar navbar-default">
                  <div className="container-fluid">
                      <div className="navbar-header">
                          <Link to="/" className="navbar-brand">珠峰培训</Link>
                      </div>
                      <div>
                          <ul className="nav navbar-nav">
                              <li className="active"><Link to="/" >首页</Link></li>
                              <li><Link to="/user" >用户管理</Link></li>
                              <li><Link to="/profile">个人设置</Link></li>
                          </ul>
                      </div>
                  </div>
              </nav>
              <div className="container">
                  <div className="row">
                      <div className="col-md-12">
                          {this.props.children}
                      </div>
                  </div>
              </div>
          </Fragment>
      )
  }
}
import React,{Component,Fragment} from 'react';
import Link from 'umi/link';
export default class User extends Component{
    render() {
        return (
            <div className="row">
                <div className="col-md-3">
                    <ul className="nav nav-stack">
                        <li><Link to="/user/list">用户列表</Link></li>
                        <li><Link to="/user/add">新增用户</Link></li>
                    </ul>
                </div>
                <div className="col-md-9">
                    {this.props.children}
                </div>
            </div>
        )
    }
}
/pages/user/list.js
import React,{Component,Fragment} from 'react';
import Link from 'umi/link';
export default class List extends Component{
    render() {
        return (
            <ul className="list-group">
                <li className="list-group-item">
                    <Link to="/user/detail/1">1</Link>
                </li>
            </ul>
        )
    }
}
pages/user/add.js
import React,{Component,Fragment} from 'react';
export default class Add extends Component{
    render() {
        return (
            <form className="form-horizontal">
                <div className="form-group">
                    <label className="control-label col-md-2">用户名</label>
                    <div className="col-md-10">
                       <input className="form-control" />
                    </div>
                </div>
                <div className="form-group">
                    <div className="col-md-10 col-offset-2">
                       <input type="submit" className="btn btn-primary"/>
                    </div>
                </div>
            </form>
        )
    }
}
import React,{Component,Fragment} from 'react';
export default class List extends Component{
  render() {
      console.log(this.props);
      return (
          <table className="table table-bordered">
              <thead>
                  <tr>
                      <td>字段</td>
                      <td>值</td>
                  </tr>
              </thead>
              <tbody>
                  <tr>
                      <td>1</td>
                      <td>张三</td>
                  </tr>
              </tbody>
          </table>
      )
  }
}
Routes 属性来实现。约定式的通过 yaml 注释添加,配置式的直接配上即可。PrivateRoute.js 的位置是相对于根目录的/**
 * title: 个人中心
 * Routes:
 *   - ./PrivateRoute.js
 */
PrivateRoute.js
import { Route ,Redirect} from 'react-router-dom';
export default ({ render, ...others }) => {
  return <Route
    {...others}
      render={props => localStorage.getItem('login')?render(props):<Redirect to={{pathname:'/login',state:{from:props.location.pathname}}}/>
    }
  />;
}
pages/login.js
import React, { Component } from 'react'
import Link from 'umi/link';
import router from 'umi/router';
export default class componentName extends Component {
  login = ()=>{
     localStorage.setItem('login','true');
     if(this.props.location.state&&this.props.location.state.from){
        router.push(this.props.location.state.from);
     }
  } 
  render() {
    return (
     <button onClick={this.login}>登录</button>
    )
  }
}
cnpm i @babel/core @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/preset-env @babel/preset-react babel-loader css-loader file-loader html-webpack-plugin react react-dom react-router-dom style-loader url-loader bootstrap@3 webpack webpack-cli webpack-dev-server -S
webpack.config.js
const path = require("path");
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  context: process.cwd(),
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env","@babel/preset-react"],
            plugins: [
              ["@babel/plugin-proposal-decorators", { legacy: true }],
              ["@babel/plugin-proposal-class-properties", { loose: true }]
            ]
          }
        }
      },
      {
          test:/\.css$/,
          use:[
              "style-loader","css-loader"
          ]
      }
    ]
  },
  plugins: [new htmlWebpackPlugin({
    template:'./src/index.html'
  })],
  devServer: {
      contentBase:path.resolve('dist')
  }
};
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>
import React from 'react';
import ReactDOM from 'react-dom';
import Router from './router';
ReactDOM.render(<Router/>,document.getElementById('root'));
import React from "react";
import { HashRouter as Router, Route } from "react-router-dom";
let routesConfig = require("./routesConfig");
function renderRoutes(routesConfig) {
  return routesConfig.map(({ path, exact = false, routes, component: RouteComponent, Routes: PrivateRoute }, index) => (
    <Route key={index} path={path} exact={exact} render={
      props => {
        let render = props => (
          <RouteComponent {...props}>
            {routes && routes.length > 0 && renderRoutes(routes)}
          </RouteComponent>
        )
        if (PrivateRoute) {
          let privateRouteProps = { render, path, exact };
          return <PrivateRoute {...privateRouteProps} />
        } else {
          return render(props);
        }
      }
    } />
  ));
}
export default props => <Router>{renderRoutes(routesConfig)}</Router>;
module.exports = [
  {
    "path": "/",
    "component": require('../layouts/index.js').default,
    "routes": [
      {
        "path": "/",
        "exact": true,
        "component": require('../pages/index.js').default
      },
      {
        "path": "/login",
        "exact": true,
        "component": require('../pages/login.js').default
      },
      {
        "path": "/profile",
        "exact": true,
        "component": require('../pages/profile.js').default,
        "Routes": require('../PrivateRoute.js').default
      }
    ]
  }
];