一、Umi 介绍
umi 是可扩展的企业级前端应用框架,Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。
create-react-app 对比:
create-react-app 是基于 webpack 的打包层方案,包含 build、dev、lint 等,他在打包层把体验做到了极致,但是不包含路由,不是框架,也不支持配置。所以,如果大家想基于他修改部分配置,或者希望在打包层之外也做技术收敛时,就会遇到困难。
与nextJs 对比:
next.js 是个很好的选择,Umi 很多功能是参考 next.js 做的。要说有哪些地方不如 Umi,我觉得可能是不够贴近业务,不够接地气。比如 antd、dva 的深度整合,比如国际化、权限、数据流、配置式路由、补丁方案、自动化 external 方面等等一线开发者才会遇到的问题。
官网链接:https://umijs.org/zh-CN/docs
二、项目创建
- 创建一个空文件夹作为项目的根目录:/umi
- 在目录终端创建项目
1、yarn create @umijs/umi-app 2、npx @umijs/create-umi
- 下载安装包
1、yarn 2、npm install
- 启动项目
yarn start
- 项目启动成功界面
二、简单使用
umi创建后已经帮我们配置好了一些插件,其中最主要的是 @umijs/preset-react,它包含了很多插件
所以项目可以直接使用antd 组件
import styles from './index.less';
import { Pagination } from 'antd';
export default function IndexPage() {
return (
<div>
<h1 className={styles.title}>Page index</h1>
<Pagination defaultCurrent={1} total={50} />
</div>
);
}
页面效果
三、项目结构
其中 public 与 config 为新建文件,用来存放静态文件及 核心配置
四、修改配置目录
五、配置路由
- 简单路由
export default [ { path: '/', component: '@/pages/index' }, { path: '/hello', component: '@/pages/hello' // 也可以写作 'hello' 相对路径,会在 src/page 下找 } ]
- 嵌套路由
export default [ { path: '/', component: '@/pages/index' }, { path: '/hello', component: '@/pages/hello' // 也可以写作 'hello' 相对路径,会在 src/page 下找 }, { path: '/user', routes: [ { path: '/user/add', component: '@/pages/user/add' } ] } ]
-
公共模版
创建公共页头和页脚 header footer 模块
在pages 同级创建 components/Header/index.tsx 文件
路由部分引入模版组件import React from 'react'; const Header = (props: any) => { return ( <div> <h3>主页/菜单项</h3> {props.children} </div> ); }; export default Header;
export default [ { path: '/', component: '@/pages/index' }, { path: '/hello', component: '@/pages/hello' // 也可以写作 'hello' 相对路径,会在 src/page 下找 }, { path: '/user', component: '@/components/Header', routes: [ { path: '/user/login', component: '@/pages/user/login' }, { path: '/user/quit', component: '@/pages/user/quit' } ] } ]
六、跳转方法
- 声明式跳转 <link to=""> 为 umi 自带的声明式跳转路由方法
import React from 'react'; import { Link } from 'umi' const Header = (props: any) => { return ( <div> <h3>主页/菜单项</h3> <Link to='/user/login'>登录</Link> /<Link to='/user/quit'>退出</Link> {props.children} </div> ); }; export default Header;
- <NavLink to=""> 专门用于导航栏,点击导航栏时,标签会默认添加 active class类,可以通过此类添加样式
- 命令式跳转 (使用 umi 的 history 函数)
import React from 'react'; import { NavLink, history} from 'umi' import './index.less' import { Space, Button } from 'antd' const Header = (props: any) => { const gotoLogin = () => { history.push('/user/login') } const gotoQuit = () => { history.push('/user/quit') } return ( <div> <h3>主页/菜单项</h3> {/* <NavLink to='/user/login'>登录</NavLink> /<NavLink to='/user/quit'>退出</NavLink> */} <Space> <Button onClick={gotoLogin}>去登陆</Button> <Button onClick={gotoQuit}>退出</Button> </Space> {props.children} </div> ); }; export default Header;
七、mock 数据与接口请求
- mock 在umi 中是默认启动的,用来模拟api 数据
- 添加 mock 文件,只要在 外层 mock 目录中的文件 都会被拆解成 mock 文件 ,新建文件mock/mock.ts
export default { 'GET /mock/list': [ { id: 1, name: '张三' }, { id: 1, name: '李四' } ] }
// 同步请求 const mockTest = () => { request('/mock/list').then(res => { console.log(res) }) } // 异步请求 const mockTest = async () => { const res = await request('/mock/list') console.log(res) }
-
MockJs 工具使用, 可以通过代码随机生成数据
引入 Mockjs 包
yarn add mockjs
import mockjs from 'mockjs' export default { 'GET /mock/citys': mockjs.mock({ 'list|100': [{name: '@city', 'value|1-100': 50, 'type|0-2': 1}] }) }
八、Dva的使用
- dva 其实就是个管理数据流的框架,可以更好的在组件之间共用数据,避免了层层 props 传参,也就是dva 是一个数据管理中心
-
符合以下规则的被认为是一个model 数据站
(1)src/models 下的文件
(2)src/pages 下,子目录中 models 目录下的文件
(3)src/pages 下,所有的 model.ts 文件(不区分大小写)
-
Model 的结构
在src 目录下创建一个 models 文件夹,内部创建一个 msg.ts 文件,结构如下
export default { namespace: "dva", // 命名空间,确保唯一 state: {}, // 存放数据 effects: {}, // 接口调用 -- 请求数据 reducers: {} // 更新状态 -- 修改数据 }
通过指令识别 是否创建成功: umi dva list model
namespace:当前model的命名空间,同时也是全局 state 上的一个属性
state:数据初始值
effects:effect 要点如下(难点)
(1)处理异步操作
(2)需要通过 put 调用 reducers 更新 state
(3)只能是 generator 函数,有 action 和 effects 两个参数。其中 effects 包含put、 call、 sellect 三个字段,put 用于触发 action, call 用于异步处理逻辑
- dva 连接, 在 pages 下创建一个 msg.tsx 文件
connect 的作用是 将数据绑定到组件上import React from 'react'; import {connect} from 'umi' const Msg = () => { return ( <div> <h1>Dva 测试</h1> </div> ) } export default connect(({msg}) => ({msg}))(Msg)
-
dva 数据使用步骤
(1)src 目录下创建 services/api.ts 文件
(2) models/msg.ts dva 文件调用上面接口保存数据import { request } from 'umi' export async function getCitys() { return request('/mock/citys') }
(3)页面内部使用dvaimport { getCitys } from '@/services/api' export default { namespace: "msg", // 命名空间,确保唯一 state: { cityList: [], }, // 存放数据 effects: { // 接口调用 -- 请求数据 //payload 获取组件中参数,例如表单提交获得到的数据 //callback 回调函数,例如提交成功后跳出一个提示框 //put 触发action //call 访问外部方法,获取外部数据 call(方法,方法传参) * queryCityList({payload, callback}, {put, call}) { //获取tags数据,yield实现异步 const res = yield call(getCitys) //调用reducers,更新state yield put({ type: 'setCityList', // 类似于 redux 中 action 的 type payload: res }) } }, // 是一个纯函数,用于处理同步操作,是唯一可以修改 state 的地方,由 action 触发,有 state 和 action 两个参数 reducers: { // 更新状态 -- 修改数据 setCityList(state, action) { return {...state, cityList: action.payload} } } }
因为 export default connect(({msg})=>({msg}))(Msg); 把数据都传到了Msg里面的props.msg, 通过其中的 dispatch 发送action获取我们的数据,通过 type: 'msg/queryCityList' 调用 effect 中的 generator 函数import React from 'react'; import {connect} from 'umi' import { Button } from 'antd' const Msg = (props: any) => { const {dispatch} = props; const list = props.msg.cityList.list || [] const getData = () => { // 使用 model 获取数据 dispatch({ type: 'msg/queryCityList', payload: null }) } return ( <div> <h1>Dva 测试</h1> <Button onClick={getData}>获取列表数据</Button> <table> <tr><th>城市名</th><th>数量</th><th>城市类型</th></tr> { list.map((item, index) => { return <tr key={index}> <td>{item.name}</td> <td>{item.value}</td> <td>{item.type}</td> </tr> }) } </table> </div> ) } export default connect(({msg}) => ({msg}))(Msg)