本文是 sorrycc/blog#18 的更新版,基于 dva@2、umi 和 umi-plugin-dva。下面会一步步引导大家如何创建一个 CURD 应用,包含查询、编辑、删除、创建,以及分页处理,数据 mock,自动处理 loading 状态等。
全部代码在 umijs/umi-dva-user-dashboard,对 dva 熟练的同学可以看代码。
最终效果:

开始之前:
- 确保 node 版本是 8.4 或以上
- 用 cnpm 或 yarn 能节约你安装依赖的时间
Step 1. 安装 dva-cli 并创建应用
先安装 dva-cli,并确保版本是 1.0.0-beta.2 或以上。
$ npm i dva-cli@next -g
$ dva -v
1.0.0-beta.2
然后创建应用:
$ dva new user-dashboard
$ cd user-dashboard
修改 .webpackrc.js,加上 "proxy" 配置:
"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
},
然后启动应用:(这个命令一直开着,后面不需要重启)
浏览器会自动开启,并打开 http://localhost:8000。

访问 http://localhost:8000/api/users ,就能访问到 http://jsonplaceholder.typicode.com/users 的数据。(由于 typicode.com 服务的稳定性,偶尔可能会失败。不过没关系,正好便于我们之后对于出错的处理)

Step 3. 生成 users 路由
umi 中文件即路由,所以我们要新增路由,新建文件即可,详见 https://umijs.org/docs/zh-Hans/router.html 。
新建 src/pages/users/page.js,内容如下:
export default () => {
return (
<div>
Users Page
</div>
)
}
然后访问 http://localhost:8000/users,你会看到 Users Page 的输出。
Step 4. 构造 users model 和 service
新增 src/pages/users/models/users.js,内容如下:
import * as usersService from '../services/users';
export default {
namespace: 'users',
state: {
list: [],
total: null,
},
reducers: {
save(state, { payload: { data: list, total } }) {
return { ...state, list, total };
},
},
effects: {
*fetch({ payload: { page } }, { call, put }) {
const { data, headers } = yield call(usersService.fetch, { page });
yield put({ type: 'save', payload: { data, total: headers['x-total-count'] } });
},
},
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname, query }) => {
if (pathname === '/users') {
dispatch({ type: 'fetch', payload: query });
}
});
},
},
};
新增 src/pages/users/services/users.js:
import request from '../../../utils/request';
export function fetch({ page = 1 }) {
return request(`/api/users?_page=${page}&_limit=5`);
}
由于我们需要从 response headers 中获取 total users 数量,所以需要改造下 src/utils/request.js:
import fetch from 'dva/fetch';
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default async function request(url, options) {
const response = await fetch(url, options);
checkStatus(response);
const data = await response.json();
const ret = {
data,
headers: {},
};
if (response.headers.get('x-total-count')) {
ret.headers['x-total-count'] = response.headers.get('x-total-count');
}
return ret;
}
切换到浏览器(会自动刷新),应该没任何变化,因为数据虽然好了,但并没有视图与之关联。但是打开 Redux 开发者工具,应该可以看到 users/fetch 和 users/save 的 action 以及相关的 state 。

Step 5. 添加界面,让用户列表展现出来
我们把组件存在 src/pages/users/components 里,所以在这里新建 Users.js 和 Users.css。具体参考这个 Commit。
需留意两件事:
- 对 model 进行了微调,加入了 page 表示当前页
- 由于 components 和 services 中都用到了 pageSize,所以提取到
src/constants.js
改完后,切换到浏览器,应该能看到带分页的用户列表。

Step 6. 添加 layout
添加 layout 布局,使得我们可以在首页和用户列表页之间来回切换。umi 里约定 layouts/index.js 为全局路由,所以我们新增 src/layouts/index.js 和 CSS 文件即可。
参考这个 Commit。
注意:
- 页头的菜单会随着页面切换变化,高亮显示当前页所在的菜单项
Step 7. 处理 loading 状态
dva 有一个管理 effects 执行的 hook,并基于此封装了 dva-loading 插件。通过这个插件,我们可以不必一遍遍地写 showLoading 和 hideLoading,当发起请求时,插件会自动设置数据里的 loading 状态为 true 或 false 。然后我们在渲染 components 时绑定并根据这个数据进行渲染。
umi-plugin-dva 默认内置了 dva-loading 插件。
然后在 src/components/Users/Users.js 里绑定 loading 数据:
+ loading: state.loading.models.users,
具体参考这个 Commit 。
刷新浏览器,你的用户列表有 loading 了没?
Step 8. 处理分页
只改一个文件 src/pages/users/components/Users.js 就好。
处理分页有两个思路:
- 发 action,请求新的分页数据,保存到 model,然后自动更新页面
- 切换路由 (由于之前监听了路由变化,所以后续的事情会自动处理)
我们用的是思路 2 的方式,好处是用户可以直接访问到 page 2 或其他页面。
参考这个 Commit 。
Step 9. 处理用户删除
经过前面的 8 步,应用的整体脉络已经清晰,相信大家已经对整体流程也有了一定了解。
后面的功能调整基本都可以按照以下三步进行:
- service
- model
- component
我们现在开始增加用户删除功能。
- service, 修改
src/pages/users/services/users.js:
export function remove(id) {
return request(`/api/users/${id}`, {
method: 'DELETE',
});
}
- model, 修改
src/pages/users/models/users.js:
*remove({ payload: id }, { call, put, select }) {
yield call(usersService.remove, id);
const page = yield select(state => state.users.page);
yield put({ type: 'fetch', payload: { page } });
},
- component, 修改
src/pages/users/components/Users.js,替换 deleteHandler 内容:
dispatch({
type: 'users/remove',
payload: id,
});
切换到浏览器,删除功能应该已经生效。
Step 10. 处理用户编辑
处理用户编辑和前面的一样,遵循三步走:
- service
- model
- component
先是 service,修改 src/pages/users/services/users.js:
export function patch(id, values) {
return request(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify(values),
});
}
再是 model,修改 src/pages/users/models/users.js:
*patch({ payload: { id, values } }, { call, put, select }) {
yield call(usersService.patch, id, values);
const page = yield select(state => state.users.page);
yield put({ type: 'fetch', payload: { page } });
},
最后是 component,详见 Commit。
需要注意的一点是,我们在这里如何处理 Modal 的 visible 状态,有几种选择:
- 存 dva 的 model state 里
- 存 component state 里
另外,怎么存也是个问题,可以:
- 只有一个 visible,然后根据用户点选的 user 填不同的表单数据
- 几个 user 几个 visible
此教程选的方案是 2-2,即存 component state,并且 visible 按 user 存。另外为了使用的简便,封装了一个 UserModal 的组件。
完成后,切换到浏览器,应该就能对用户进行编辑了。
Step 11. 处理用户创建
相比用户编辑,用户创建更简单些,因为可以共用 UserModal 组件。和 Step 10 比较类似,就不累述了,详见 Commit 。
到这里,我们已经完成了一个完整的 CURD 应用。如果感兴趣,可以进一步看下 dva 和 umi 的资料:
(完)
本文是 sorrycc/blog#18 的更新版,基于 dva@2、umi 和 umi-plugin-dva。下面会一步步引导大家如何创建一个 CURD 应用,包含查询、编辑、删除、创建,以及分页处理,数据 mock,自动处理 loading 状态等。
全部代码在 umijs/umi-dva-user-dashboard,对 dva 熟练的同学可以看代码。
最终效果:
开始之前:
Step 1. 安装 dva-cli 并创建应用
先安装 dva-cli,并确保版本是 1.0.0-beta.2 或以上。
然后创建应用:
$ dva new user-dashboard $ cd user-dashboardStep 2. 配置代理,能通过 RESTFul 的方式访问 http://localhost:8000/api/users
修改
.webpackrc.js,加上"proxy"配置:然后启动应用:(这个命令一直开着,后面不需要重启)
浏览器会自动开启,并打开 http://localhost:8000。
访问 http://localhost:8000/api/users ,就能访问到 http://jsonplaceholder.typicode.com/users 的数据。(由于 typicode.com 服务的稳定性,偶尔可能会失败。不过没关系,正好便于我们之后对于出错的处理)
Step 3. 生成 users 路由
umi 中文件即路由,所以我们要新增路由,新建文件即可,详见 https://umijs.org/docs/zh-Hans/router.html 。
新建
src/pages/users/page.js,内容如下:然后访问 http://localhost:8000/users,你会看到
Users Page的输出。Step 4. 构造 users model 和 service
新增
src/pages/users/models/users.js,内容如下:新增
src/pages/users/services/users.js:由于我们需要从 response headers 中获取 total users 数量,所以需要改造下
src/utils/request.js:切换到浏览器(会自动刷新),应该没任何变化,因为数据虽然好了,但并没有视图与之关联。但是打开 Redux 开发者工具,应该可以看到
users/fetch和users/save的 action 以及相关的 state 。Step 5. 添加界面,让用户列表展现出来
我们把组件存在 src/pages/users/components 里,所以在这里新建 Users.js 和 Users.css。具体参考这个 Commit。
需留意两件事:
src/constants.js改完后,切换到浏览器,应该能看到带分页的用户列表。
Step 6. 添加 layout
添加 layout 布局,使得我们可以在首页和用户列表页之间来回切换。umi 里约定 layouts/index.js 为全局路由,所以我们新增
src/layouts/index.js和 CSS 文件即可。参考这个 Commit。
注意:
Step 7. 处理 loading 状态
dva 有一个管理 effects 执行的 hook,并基于此封装了 dva-loading 插件。通过这个插件,我们可以不必一遍遍地写 showLoading 和 hideLoading,当发起请求时,插件会自动设置数据里的 loading 状态为 true 或 false 。然后我们在渲染 components 时绑定并根据这个数据进行渲染。
umi-plugin-dva 默认内置了 dva-loading 插件。
然后在
src/components/Users/Users.js里绑定 loading 数据:+ loading: state.loading.models.users,具体参考这个 Commit 。
刷新浏览器,你的用户列表有 loading 了没?
Step 8. 处理分页
只改一个文件
src/pages/users/components/Users.js就好。处理分页有两个思路:
我们用的是思路 2 的方式,好处是用户可以直接访问到 page 2 或其他页面。
参考这个 Commit 。
Step 9. 处理用户删除
经过前面的 8 步,应用的整体脉络已经清晰,相信大家已经对整体流程也有了一定了解。
后面的功能调整基本都可以按照以下三步进行:
我们现在开始增加用户删除功能。
src/pages/users/services/users.js:src/pages/users/models/users.js:src/pages/users/components/Users.js,替换deleteHandler内容:切换到浏览器,删除功能应该已经生效。
Step 10. 处理用户编辑
处理用户编辑和前面的一样,遵循三步走:
先是 service,修改
src/pages/users/services/users.js:再是 model,修改
src/pages/users/models/users.js:最后是 component,详见 Commit。
需要注意的一点是,我们在这里如何处理 Modal 的 visible 状态,有几种选择:
另外,怎么存也是个问题,可以:
此教程选的方案是 2-2,即存 component state,并且 visible 按 user 存。另外为了使用的简便,封装了一个
UserModal的组件。完成后,切换到浏览器,应该就能对用户进行编辑了。
Step 11. 处理用户创建
相比用户编辑,用户创建更简单些,因为可以共用
UserModal组件。和 Step 10 比较类似,就不累述了,详见 Commit 。到这里,我们已经完成了一个完整的 CURD 应用。如果感兴趣,可以进一步看下 dva 和 umi 的资料:
(完)