UmiJS笔记

简介

  • Umi,中文可发音为乌米,是可扩展的企业级前端应用框架

  • 该框架集合了reactdvaantd

  • 支持Typescript

  • 支持类似于next.js的约定式路由,也支持配置式路由

常用命令

  • npm install umi -gnpm全局安装umi
  • yarn global add umiyarn全局安装umi
  • umi g page index:创建index首页,会创建一个pages/文件夹,里面有index.jsindex.css
  • umi g page users/index:会创建一个pages/users/文件夹,里面有index.jsindex.css

项目搭建

  • 全局安装yarnnpm install yarn -g

  • 使用yarn安装umiyarn global add umi

  • cd到项目目录下

  • 构建umi项目:yarn create umi

  • (或者)构建umi项目:yarn create @umijs/umi-app

  • 安装依赖:yarn

  • 启动项目:yarn start

目录结构

├── config/ ├── config.js // umi 配置,同 .umirc.js,二选一 ├── dist/ // 默认的 build 输出目录 ├── mock/ // mock 文件所在目录,基于 express ├── public/ // 全局相对路径文件 └── src/ // 源码目录 ├── assets/ // 静态文件 ├── components/ // 全局共用组件 ├── layouts/index.js // 全局布局文件 ├── models/ // 全局models文件,存放全局共用数据store ├── pages/ // 页面目录,业务组件 ├── .umi/ // dev 临时目录,需添加到 .gitignore ├── .umi-production/ // build 临时目录,会自动删除 ├── index/ // 首页模块 ├── manager/ // 管理端模块 ├── components/ // 管理端-局部公共组件 ├── models/ // 管理端-局部models,存放manager的store ├── services/ // 管理端-局部services,存放manager的接口 ├── index.js // 业务组件index ├── page.js // 业务组件page ├── _layout.js // 局部入口文件 ├── 404.js // 404 页面 ├── document.ejs // 如果有这个文件,会覆盖默认的HTML模板,至少需要有 <div id="root"></div> ├── services/ // 全局services文件,存放全局公共接口 ├── utils/ // 全局工具类 ├── global.css // 约定的全局样式文件,自动引入,也可以用 global.less ├── global.js // 约定的全局Js文件,自动引入,可以在这里加入 polyfill ├── app.js // 运行时配置文件 ├── .umirc.js // umi 配置,同 config/config.js,二选一 ├── .env // 环境变量 └── package.json

路由

约定式路由

  • umi约定,在pages目录下的.js/.jsx会自动生成路由,除开在配置文件plugins/routes中被exclude的目录或文件,文件的路径即路由

  • src/layout/为全局layout,默认全局入口文件,配置式路由下无效。

嵌套路由

  • pages/下任何文件下的_layout.js即当前文件夹路由下的入口文件,必须先经过_layout.js才能进入当前文件夹路由
  • _layout.js中必须有props.children。通过props.children渲染子组件

嵌套路由示例/users

  • src/pages/目录下创建users/目录

  • src/page/users/目录下创建_layout.jsx文件

// props.children 引入子组件,如果没有创建子组件,则会报错 export default function(props) { return ( <div> <h1>Page users _layout</h1> <div>{props.children}</div> </div> ) }
  • src/page/users/目录下创建index.jsx文件

  • 访问嵌套路由:http://localhost:8000/users

动态路由

  • 文件的命名以$为开头

  • users/$id.js对应路由为/users/:id。页面组件中用props.match.params.id获取路由参数

  • 如果带$后缀,则为可选动态路由。比如:users/$id$.js

注释扩展路由

约定路由文件的首个注释如果包含 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' ], }, ]

配置式路由

  • 在配置文件 .umirc.(ts|js) 或者 config/config.(ts|js)中配置

  • 此配置项存在时则不会对src/pages目录做约定式的解析

  • component是相对于src/pages目录的

// 路由示例 export default { routes: [ { path: '/', component: '../layouts/index', routes: [ { path: '/user', redirect: '/user/login' },//redirect,避免只渲染_layout.js { path: '/user/login', component: './user/login' }, { path: '/manager', component: '../pages/management/_layout.js', routes: [ { path: '/manager/system', component: '../pages/management/manager/system', Routes: ['./routes/PrivateRoute.js'] } } ], }, ], };

权限路由

  • 路由通过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 import Redirect from 'umi/redirect' export default (props) => { if(Math.random()>0.5) { return <Redirect to="/login" /> //没有登录时,重定向到登录页 } // 登录成功时,显示子路由的页面组件 return ( <div> { props.children } </div> ); }

跳转路由

  • Link方式
import Link from 'umi/link'; <link to="/list">Go to list page</Link>
  • router方式
import router from 'umi/router'; router.push({ pathname: '/list', query: { a: b } })

withRouter

  • 未经路由跳转的组件,如子组件想拿到路由的相关信息locationhistorymatch等时可用,经路由跳转的页面则默认已有路由信息

  • 比如:layouts/index.js 里如果用了 connect 传数据,需要用 umi/withRouter 高阶一下

import withRouter from 'umi/withRouter'; export default withRouter(connect()(Layout));

路由动效

使用插件react-transition-group

  • 修改layout组件,渲染子组件时用TransitionGroupCSSTransition包裹一层,并以location.keykey
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; }

使用dva

  • dva主要是软件分层的概念
  • components负责与用户直接交互:渲染页面、接收用户的操作输入,侧重于展示型和交互逻辑
  • models负责处理业务逻辑,可以理解成一个维护页面数据状态的对象,为展示层做数据、状态的读写等操作
  • services主要负责与HTTP做接口对接,跟后端做数据交互,读写数据

dva的数据流向

  1. viewdispatch操作action
  2. 触发modeleffect中相应方法
  3. 触发call发起services层请求,获取接口数据
  4. 触发put发起reducer处理相应的action更新数据
  5. 更新model层中state
  6. 触发view层的render方法进行重新渲染
  7. 页面更新
  • dva已经融合进了umi,在config/config.js中打开dva的开关
plugins: [ [ "umi-plugin-react", { antd: true, dva: true } ] ]
  • umi/dva是基于reduxredux-saga的数据流方案

  • umi会按照约定的目录来自动注册model,且文件名会被识别为modelnamespace

model分为全局model页面model

  • src/models/**/*.jsglobal model
  • src/pages/**/models/**/*.jspage model
  • global model 全量载入, page modelproduction 时按需载入,在 development 时全量载入
  • page modelpage js 所在路径下 models/**/*.js 的文件
  • page model 会向上查找,比如 page jspages/a/b.js ,他的 page modelpages/a/b/models/**/*.js + pages/a/models/**/*.js ,依次类推
  • 约定 model.js 为单文件 model,解决只有一个 model 时不需要建 models 目录的问题,有 model.js 则不去找 models/**/*.js
  • dvaconnectreduxconnectdvamodelredux-saga简化版,更易用
import React from 'react'; import { connect } from 'dva'; import ProductList from '@/components/products/list/index' @connect( ({ products }) => ({ products }) ) class Products extends Component { componentDidMount() { console.log(this.props.products) } render() { return ( <div> <h2>List of Products</h2> </div> ); } } export default Products

dva使用可参考:

mock

  • 约定mock/目录里所有的.js文件会被解析为mock文件
export default { // 支持值为 Object 和 Array 'GET /api/users': { users: [1, 2] }, // GET POST 可省略 '/api/users/1': { id: 1 }, // 支持自定义函数,API 参考 express@4 'POST /api/users/create': (req, res) => { res.end('OK'); }, };

使用Mock.js

import mockjs from 'mockjs' export default { // 使用 mockjs 等三方库 'GET /api/tags': mockjs.mock({ 'list|100': [{ name: '@city', 'value|1-100': 50, 'type|0-2': 1 }], }), }

添加跨域请求头

export default { 'POST /api/users/create': (req, res) => { // ... res.setHeader('Access-Control-Allow-Origin', '*'); // ... }, }

模拟延迟

  • 手动添加setTimeout模拟延迟
export default { 'POST /api/forms': (req, res) => { setTimeout(() => { res.send('Ok'); }, 1000); }, }
  • 使用roadhog-api-doc#delay统一处理
import { delay } from 'roadhog-api-doc'; const proxy = { 'GET /api/project/notice': getNotice, 'GET /api/activities': getActivities, 'GET /api/rule': getRule, 'GET /api/tags': mockjs.mock({ 'list|100': [{ name: '@city', 'value|1-100': 50, 'type|0-2': 1 }] }), 'GET /api/fake_list': getFakeList, 'GET /api/fake_chart_data': getFakeChartData, 'GET /api/profile/basic': getProfileBasicData, 'GET /api/profile/advanced': getProfileAdvancedData, 'POST /api/register': (req, res) => { res.send({ status: 'ok' }); }, 'GET /api/notices': getNotices, }; // 调用 delay 函数,统一处理 export default delay(proxy, 1000);

创作不易,若本文对你有帮助,欢迎打赏支持作者!

 分享给好友: