安装(官方文档:https://umijs.org/zh):
yarn global add umi
使用:
使用umi -v可查看版本,确保全局安装没问题
umi g page <文件名>
//创建包含目录的页面home/index.js home/index.css
umi g page home/index
//创建dva modle文件(如果项目是用yarn create umi创建的则无法使用该命令)
umi g dva:modle <文件名> //umi+dva的项目中可以不用在modle文件中指定namespace,默认取文件名
通常情况下我们会使用yarn create umi来创建项目结构,在创建的时候会提示你做出一些关于项目的选择:
确定后,会根据你的选择自动创建好目录和文件,然后yarn install安装依赖。
常用配置:
更改主题色和自定义less全局变量,在.umirc文件中加入:
"theme": {
"primary-color": "#1DA57A",//更改主题色
"main_width": "1440px",//自定义全局样式变量
},
启用组件按需加载,在.umirc文件umi-plugin-react插件配置中加入dynamicImport配置(同样基于react-loadble插件实现的):
dynamicImport: {
webpackChunkName: true,//是否打包时将分割出来的文件命名让其有意义
loadingComponent: './components/Loading.js',//loading组件
level: 2, //根据几级路由做按需编译,值越大按需编译的越详细,默认是会根据路由层级来动态判断等级的,所以一般可以不指定
},
项目默认语言umi-plugin-react插件配置设置:
locale: {
default: 'zh-CN'
},
umi-plugin-react插件配置还可以配置dva的immer,让其在reducer中不需要再return修改后state,而是可以直接修改即可生效:
dva: {
immer: true,
hmr: true
},
//在reducer中:
reducer:{//不启用immer
addCount(state, action){
state.count += 1
return state
}
}
reducer:{//启用immer
addCount(state, action){
state.count += 1
}
}
//这只是简单基本数据类型的改变实例,在某些情况下会让你少写更多代码
解决浏览器缓存问题,当项目迭代一个版本更新部署到服务器时防止浏览器读取之前的缓存文件,在.umirc中加入:
hash: true,//默认是false
//打包后的文件名会由xxx.async.js变为xxx.0aee2612.async.js每build一次hash值都不一样
跨域代理proxy:
proxy: {
'/server/api/': {
target: 'https://preview.pro.ant.design/',
changeOrigin: true,
pathRewrite: { '^/server': '' },
},
},
umi通过chainWebpack暴露出了webpack的配置,从而我们可以修改:
chainWebpack(config, { webpack }) {
config.module.rules.store.delete('eslint')//禁用eslint
}
//因为不想修改node_modules,我也是通过打印config对象摸索,尝试修改,果然成功。这里因为store是个Map类型的对象,所以只能用delete来删除key
在原始dva项目中直接通过app.js中导出的dva实例对象即可获取dispatch,但在umi中dva对象实例化配置过程全部是由umi自动生成,这里umi将dva实例对象注入到了window.g_app._store中。
实际项目请求封装示例:
// request.js
import axios from 'axios'
import { notification, message } from 'antd'
import history from 'umi/router';
const ipConfig = require('../apiConfig')
message.config({
top: 200,
duration: 3,
maxCount: 3,
})
/**
* 一、功能:
* 1. 统一拦截http错误请求码;
* 2. 统一拦截业务错误代码;
* 3. 统一设置请求前缀
* |-- 每个 http 加前缀 baseURL = /api,从配置文件中获取 apiPrefix
*
* 二、引包:
* |-- axios:http 请求工具库
* |-- notification:Antd组件 > 处理错误响应码提示信息
* |-- history:dva/router对象,用于路由跳转,错误响应码跳转相应页面
* |-- store:dva中对象,使用里面的 dispatch 对象,用于触发路由跳转
*/
// 设置全局参数,如响应超市时间,请求前缀等。
axios.defaults.timeout = 30000
if(process.env.NODE_ENV == 'development'){
// axios.defaults.baseURL = '/API'
axios.defaults.baseURL = ipConfig['dev']
}else{
axios.defaults.baseURL = ipConfig['build']
}
axios.defaults.withCredentials = true
// 状态码错误信息
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
}
// 添加一个请求拦截器,用于设置请求过渡状态
axios.interceptors.request.use((config) => {
const token = localStorage.user ? JSON.parse(localStorage.user).token : null
const visitToken = localStorage.visitToken
config.headers.token = token ? token : visitToken
return config
}, (error) => {
return Promise.reject(error)
})
// 添加一个返回拦截器
axios.interceptors.response.use((response) => {
return response
}, (error) => {
return Promise.reject(error)
})
export default function request (opt) {
if(!opt.data) opt.data = {}
if(opt.method == 'GET') opt.params = opt.data
// 调用 axios api,统一拦截
return axios(opt)
.then((response) => {
// >>>>>>>>>>>>>> 请求成功 <<<<<<<<<<<<<<
// if(process.env.NODE_ENV == 'development'){
// if(!response.data.status){
// notification.error({
// message: `${opt.url}-${status}`,
// description: 'msg: '+response.data.msg,
// })
// }else{
// notification.success({
// message: `${opt.url}-${status}`,
// description: 'msg: '+response.data.msg+' data:'+JSON.stringify(response.data.data),
// })
// }
// }
if(!response.data.status && (response.data.code == '000009' || response.data.code == '700000')) {//登陆过期
window.g_app._store.dispatch({
type: 'global/common',
payload:{showLoginModal: true}
})
message.info(response.data.msg)
}
return response.data;
})
.catch((error) => {
// >>>>>>>>>>>>>> 请求失败 <<<<<<<<<<<<<<
// 请求配置发生的错误
if (!error.response) {
console.dir(error)
if(error.message.indexOf('timeout of')>-1) alert('请求超时!')
}
// 响应时状态码处理
const status = error.response.status;
const errortext = codeMessage[status] || error.response.statusText;
notification.error({
message: `请求错误- ${status}`,
description: errortext,
})
// if (status === 401) {
// history.push('/user/login')
// } else if (status === 403) {
// history.push('/exception/403')
// } else if (status <= 504 && status >= 500) {
// history.push('/exception/500')
// } else if (status >= 404 && status < 422) {
// history.push('/exception/404')
// }
// return { code: status, message: errortext }
})
}