Umi基本使用
安装
npm install umi -g
项目目录
md umi-app
cd umi-app
新建 index页
umi g page index
umi g page about
起服务
umi dev
动态路由
以$开头的文件或目录
umi g page users/'$id'
获取参数和以前写法相同
export default function({match}) {
return (
<div>
<h1>user id: {match.params.id}</h1>
</div>
);
}
嵌套路由
目录下面出现_layout组件则会转换路由配置为嵌套路由
// 创建父组件 umi g layout ./users
export default function(props) {
return (
<div>
<h1>Page _layout</h1>
<div>{props.children}</div>
</div>
)
}
// 创建兄弟组件 umi g page users/index
页面跳转
// 用户列表跳转至用户详情页, users/index.js
import Link from "umi/link";
import router from "umi/router";
export default function() {
return (
<div className={styles.normal}>
<h1>用户列表</h1>
<ul>
{users.map(u => (
// 声明式
// <li key={u.id}>
// <Link to={`/users/${u.id}`}>{u.name}</Link>
// </li>
// 命令式
<li key={u.id} onClick={()=>router.push(`/users/${u.id}`)}>{u.name}</li>
))}
</ul>
</div>
);
}
404页面
约定 pages/404.js 为 404 页面,需返回 React 组件。
创建404页面: umi g page ./404
布局页
约定 src/layouts/index.js 为全局路由,返回一个 React 组件,通过 props.children 渲染子组件。
创建布局页面: umi g page …/layouts/index
export default function(props) {
return (
<div className={styles.normal}>
<h1>布局页面</h1>
{props.children}
</div>
);
}
针对特定路由指定布局页
if (props.location.pathname === '/404') {
return <SimpleLayout>{ props.children }</SimpleLayout>
}
通过注释扩展路由
约定路由文件的首个注释如果包含 yaml 格式的配置,则会被用于扩展路由。
权限路由,about.js
/**
* title: About Page
* Routes:
* - ./routes/PrivateRoute.js
*/
创建./routes/PrivateRoute.js
import Redirect from "umi/redirect";
export default props => {
if (new Date().getDay() % 2 === 1) {
// 单数日需要登陆
return <Redirect to="/login"/>;
}
return (
<div>
<div>PrivateRoute</div>
{props.children}
</div>
);
};
创建登录页面验证: umi g page login
安装umi-plugin-react
npm install umi-plugin-react -D
配置
创建配置文件: .umirc.js
export default {
plugins: [
['umi-plugin-react', {
dva: true,
}]
]
}
创建model
新建src/models/goods.js
export default {
namespace: 'goods', // model的命名空间,区分多个model
state: [{ title: "苹果" },{ title: "橘子" }], // 初始状态
effects:{}, // 异步操作
reducers: {} // 更新状态
}
使用状态
类似redux,使用connect获取数据状态并映射给组件
创建页面goods.js: umi g page goods
export default connect(
state => ({
goodsList: state.goods // 获取指定命名空间的模型状态
}),
)(function({ goodsList }) {
return (
<div className={styles.normal}>
<h1>Page goods</h1>
<ul>
{goodsList.map(good => (
<li key={good.title}>{good.title}</li>
))}
</ul>
</div>
);
});
更新模型src/models/goods.js
export default {
reducers: {
addGood(state, action) {
return [...state, {title: action.payload}];
}
}
}
调用reducer:goods.js
export default connect(
state => ({}),
{
addGood: title => ({
type: "goods/addGood", // action的type需要以命名空间为前缀+reducer名称
payload: title
})
}
)(function({ goodsList, addGood }) {
return (
<div className={styles.normal}>
<button onClick={() => addGood("商品" + new Date().getTime())}>
添加商品
</button>
</div>
);
});
数据mock:模拟数据接口
mock目录和src平级,新建mock/goods.js
let data = [
{title:"苹果"},
{title:"橘子"}
];
export default {
// "method url": Object 或 Array
// "get /api/goods": { result: data },
// "method url": (req, res) => {}
'get /api/goods': function (req, res) {
setTimeout(() => {
res.json({ result: data })
}, 250)
},
}
effect处理异步:基于redux-saga,使用generator函数来控制异步流程
请求接口,models/goods.js
// 首先安装axios
import axios from 'axios';
// api
function getGoods(){
return axios.get('/api/goods')
}
export default {
state: [
// {title:"苹果"},
// {title:"梨子"}
],
effects: { // 副作用操作,action-动作、参数等,saga-接口对象
*getList(action, {call, put}){
const res = yield call(getGoods)
yield put({ type: 'initGoods', payload: res.data.result })
}
},
reducers: {
initGoods(state,{payload}){
return payload
}
}
}
组件调用,goods.js
import {useEffect} from 'react';
export default connect(
state => ({}),
{
addGood: title => ({}),
getList: () => ({ // 获取数据
type: "goods/getList"
}),
}
)(function({ goodsList, addGood,getList }) {
useEffect(()=>{ // 调用一次
getList();
},[])
return ();
});
加载状态
利用内置的dva-loading实现,获取加载状态,goods.js
connect(
state => ({
loading: state.loading // 通过loading命名空间获取加载状态
}),
{...}
)(function({ goodsList, addGood, getList, loading }) {
console.log(this.props.loading);
if (this.props.loading.models.goods) {
return <div>加载中...</div>
}
...
}