umiJS学习

安装umiJS

小提示一句,如果说经常把yarn和npm混用的话会出现一些奇奇怪怪的问题,具体什么问题可以自己摸索

// yarn or npm 
yarn global add umi # or npm install -g umi

创建一个项目

  • 新建项目目录
  • 使用umi新建页面
    • 使用umi命令创建
    • 自己手动创建
  • 启动项目
  • 打包发布
// 1.新建目录
mkdir myapp
cd myapp// 2.初始化项目
yarn create umi// 出现需要的功能,可以选择,目前集成了antd,code splitting,pwa,dll,hard source
// a = 全选
// i = 全选 || 取消全选
// 空格 = 单项选择// 初始化目录结构
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2018/10/18     14:42                mock
d-----       2018/10/18     14:42                src
-a----       2018/10/18     14:42            245 .editorconfig
-a----       2018/10/18     14:42             13 .env
-a----       2018/10/18     14:42             37 .eslintrc
-a----       2018/10/18     14:42            244 .gitignore
-a----       2018/10/18     14:42             70 .prettierignore
-a----       2018/10/18     14:42            175 .prettierrc
-a----       2018/10/18     14:42            337 .umirc.js
-a----       2018/10/18     14:42            688 package.json// 4.安装依赖
yarn // 5.启动项目
yarn start
在浏览器输出 http://localhost:8000 查看页面

目录以及约定

为了简化操作,可以尽可能的使用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

ES6语法支持

配置文件、mock 文件等都有通过 @babel/register 注册实时编译,所以可以和 src 里的文件一样,使用 ES6 的语法和 es modules 。

dist

打包静态文件默认输出路径,可以通过outputPath修改

Mock

约定根目录下所有的.js文件会被解析为mock文件

比如在mock目录内创建一个user.js,文件内容如下:

export default {'/api/users': ['a', 'b'],
}

然后在浏览器里访问 http://localhost:8000/api/users 就可以看到 ['a', 'b'] 了。

src

约定src为源码目录,但是可选,简单项目可以不加src这层目录

比如下边的两种结构相同

+ src+ pages- index.js+ layouts- index.js
- .umirc.js
+ pages- index.js
+ layouts- index.js
- .umirc.js

layouts布局

全局布局,实际上是路由外嵌套了一层

  • 我们的路由

    [{ path: '/', component: './pages/index' },{ path: '/users', component: './pages/users' },
    ]
    
  • 如果有layouts的路由

    [{ path: '/', component: './layouts/index', routes: [{ path: '/', component: './pages/index' },{ path: '/users', component: './pages/users' },] }
    ]
    

src/pages

约定 pages 下所有的 (j|t)sx? 文件即路由。关于更多关于约定式路由的介绍,请前往路由章节。

src/pages/404.js

404 页面。注意开发模式下有内置 umi 提供的 404 提示页面,所以只有显式访问 /404 才能访问到这个页面

src/pages/document.ejs

有这个文件时,会覆盖默认的 HTML 模板。需至少包含以下代码,

<div id="root"></div>

src/pages/.umi

这是 umi dev 时生产的临时目录,默认包含 umi.jsrouter.js,有些插件也会在这里生成一些其他临时文件。可以在这里做一些验证,但请不要直接在这里修改代码,umi 重启或者 pages 下的文件修改都会重新生成这个文件夹下的文件。

src/global.(j|t)sx?

在入口文件最前面被自动引入,可以考虑在此加入 polyfill

src/global.(css|less|sass|scss)

这个文件不走 css modules,自动被引入,可以写一些全局样式,或者做一些样式覆盖。

.umirc.js 和 config/config.js

umi 的配置文件,二选一。

路由

约定式路由

  • 假设 pages 目录结构如下:

    + pages/+ users/- index.js- list.js- index.js
    
  • umi 会自动生成路由配置如下:

    [{ path: '/', component: './pages/index.js' },{ path: '/users/', component: './pages/users/index.js' },{ path: '/users/list', component: './pages/users/list.js' },
    ]
    
  • 动态路由

    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' },
      ]
      

可选的动态路由

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

    • 比如以下结构:

      + pages/+ users/- $id$.js- index.js
      
    • 会生成路由配置如下:

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

嵌套路由

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

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

  • 相同的全局layout
export default function(props) {return (<><Header />{ props.children }<Footer /></>);
}
  • 不同的layout
export default function(props) {if (props.location.pathname === '/login') {return <SimpleLayout>{ props.children }</SimpleLayout>}return (<><Header />{ props.children }<Footer /></>);
}

404 路由

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

export default () => {return (<div>I am a customized 404 page</div>);
};// 注意:开发模式下,umi 会添加一个默认的 404 页面来辅助开发,但你仍然可通过精确地访问 /404 来验证 404 页面。

通过注释扩展路由

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

文档目录

+ pages/- index.js

如果 pages/index.js 里包含:

/*** 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' ],},
]

配置式路由

如果你倾向于使用配置式的路由,可以配置 routes此配置项存在时则不会对 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' }]},],
};// 注意:component 是相对于 src/pages 目录的

权限路由

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

比如有以下配置:

[{ path: '/', component: './pages/index.js' },{ path: '/list', component: './pages/list.js', Routes: ['./routes/PrivateRoute.js'] },
]// 然后 umi 会用 ./routes/PrivateRoute.js 来渲染 /list。

./routes/PrivateRoute.js 文件示例

export default (props) => {return (<div><div>PrivateRoute (routes/PrivateRoute.js)</div>{ props.children }</div>);
}

路由过度动画

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

  • 安装依赖
 yarn add react-transition-group

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

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>
)

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

.fade-enter {opacity: 0;z-index: 1;
}.fade-enter.fade-enter-active {opacity: 1;transition: opacity 250ms ease-in;
}
  • 可以配合Animate.css使用

新建页面

  • 使用命令行创建,加入我们希望访问的路径是/user/info,命令如下:

    umi g page user/info
    // 等待命令行先创建成功以后,就可以在浏览器访问该页面了
    
  • 手动创建, 自己的创建目录,页面文件

在页面间跳转

  • 声明式
import Link from 'umi/link';export default () => (<Link to="/list">Go to list page</Link>
);
  • 命令式
import router from 'umi/router';function goToListPage() {router.push('/list');
}

配置

umi 允许在 .umirc.jsconfig/config.js (二选一,.umirc.js 优先)中进行配置,支持 ES6 语法。

简单的例子:


// ref: https://umijs.org/config/
import {resolve} from "path";
export default {plugins: [// ref: https://umijs.org/zh/plugin/umi-plugin-react.html['umi-plugin-react', {antd: true,dva: {immer: true, //default:falsedynamicImport: {webpackChunkName: true,loadingComponent: './components/Loading.js', // 配置动态加载modelhmr: true}},dynamicImport: {  // 配置动态加载webpackChunkName: true,loadingComponent: './components/Loading.js',level: 1},targets: {ie: 10,},title: 'iss2',dll: {exclude: [],include: ["dva", "dva/router", "dva/saga", "dva/fetch", "antd/es"],},// 配置渐进增强pwa: false,routes: {exclude: [],},esLint: true,// hardSource: /* isMac */process.platform === 'darwin',title: 'ISS', //页面展示title}],[ // 自定义插件 插件加载顺序是按照我们配置的顺序加载'./plugins/initMeta.js']],alias: {'@': resolve(__dirname, './src'),"utils": resolve(__dirname,"./src/utils"),},// fastClick: true, // 移动端延迟300毫秒的问题// theme: {//   "@primary-color": "#1DA57A"// },// routes: [],theme: "./theme/theme.config.js",chainWebpack(config) {config.module.rule('svg').test(/.svg$/i).use('svg-sprite-loader').loader(require.resolve('svg-sprite-loader'));// 配置eslintconfig.module.rule('eslint').test(/.(js|mjs|jsx)$/).enforce('pre').use(['eslint-loader'], {options: {formatter: require('react-dev-utils/eslintFormatter'), eslintPath: require('eslint')}}).loader(require.resolve('eslint-loader'))},// 接口代理示例proxy: {"/api/v1/weather": {"target": "https://api.seniverse.com/","changeOrigin": true,"pathRewrite": { "^/api/v1/weather": "/v3/weather" }},"/api/v2": {"target": "http://192.168.0.110","changeOrigin": true,"pathRewrite": { "^/api/v2" : "/api/v2" }}},
}

按需加载

出于性能的考虑,我们会对模块和组件进行按需加载。

  • 按需加载组件
// 通过 umi/dynamic 接口实现,比如:
import dynamic from 'umi/dynamic';const delay = (timeout) => new Promise(resolve => setTimeout(resolve, timeout));
const App = dynamic({loader: async function() {await delay(/* 1s */1000);return () => <div>I will render after 1s</div>;},
});
  • router实例
import dynamic from 'umi/dynamic';let routes = [{"path": "/","component": dynamic({ loader: () => import(/* webpackChunkName: "layouts__index" */'../../layouts/index.js'), loading: require('E:/iss-website/src/components/Loading.js').default  }),"routes": [{"path": "/","exact": true,"component": dynamic({ loader: () => import(/* webpackChunkName: "layouts__index" */'../index.js'), loading: require('E:/iss-website/src/components/Loading.js').default  }),"_title": "ISS","_title_default": "ISS"},{"path": "/login","exact": true,"component": dynamic({ loader: () => import(/* webpackChunkName: "layouts__index" */'../login/index.js'), loading: require('E:/iss-website/src/components/Loading.js').default  }),"_title": "ISS","_title_default": "ISS"}],"_title": "ISS","_title_default": "ISS"},{"component": () => React.createElement(require('E:/iss-website/node_modules/umi-build-dev/lib/plugins/404/NotFound.js').default, { pagesPath: 'src/pages', hasRoutesInConfig: false }),"_title": "ISS","_title_default": "ISS"}
];
export default routes
  • 按需加载模块
import('g2').then(() => {// do something with g2
});

部署发布

  • 默认在命令行使用命令打包,然后拿到静态文件目录部署在服务器

  • 静态资源在非根目录或 cdn

    这时,就需要配置 publicPath。至于 publicPath 是啥?具体看 webpack 文档,把他指向静态资源(js、css、图片、字体等)所在的路径。

    export default {publicPath: "http://yourcdn/path/to/static/"
    }
    
// 打包文件
yarn build ./dist
├── index.html
├── list.html
└── static├── pages__index.5c0f5f51.async.js├── pages__list.f940b099.async.js├── umi.2924fdb7.js└── umi.cfe3ffab.css// 注意:静态化暂不支持有变量路由的场景。
  • HTML 后缀

有些静态化的场景里,是不会自动读索引文件的,比如支付宝的容器环境,那么就不能生成这种 html 文件

├── index.html
├── list
│   └── index.html// 而是生成如下页面
├── index.html
└── list.html
  • 配置方式是在 .umirc.js 里
export default {exportStatic: {htmlSuffix: true,},
}
  • umi build 会生成
./dist
├── index.html
├── list.html
└── static├── pages__index.5c0f5f51.async.js├── pages__list.f940b099.async.js├── umi.2924fdb7.js└── umi.cfe3ffab.css
  • 静态化后输出到任意路径
export default {exportStatic: {htmlSuffix: true,dynamicRoot: true,},
}

nginx 配置

server {listen       8082;server_name  localhost;location / {root   E:/mix_vue/dist;#定位到项目的目录#index  index.html index.htm;try_files $uri $uri/ /index.html;#根据官网这规则配置}location ~ .php${proxy_pass	http://mixVueServer;#根据后端语言做反向代理处理跨域问题proxy_set_header	Host	$host;proxy_set_header	X-Real-IP	$remote_addr;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}

Published by

风君子

独自遨游何稽首 揭天掀地慰生平