Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

umi + dva,完成用户管理的 CURD 应用 #62

Open
sorrycc opened this issue Feb 22, 2018 · 159 comments
Open

umi + dva,完成用户管理的 CURD 应用 #62

sorrycc opened this issue Feb 22, 2018 · 159 comments
Labels

Comments

@sorrycc
Copy link
Owner

sorrycc commented Feb 22, 2018

本文是 sorrycc/blog#18 的更新版,基于 dva@2、umiumi-plugin-dva。下面会一步步引导大家如何创建一个 CURD 应用,包含查询、编辑、删除、创建,以及分页处理,数据 mock,自动处理 loading 状态等。

全部代码在 umijs/umi-dva-user-dashboard,对 dva 熟练的同学可以看代码。

最终效果:

开始之前:

  • 确保 node 版本是 8.4 或以上
  • cnpmyarn 能节约你安装依赖的时间

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

Step 2. 配置代理,能通过 RESTFul 的方式访问 http://localhost:8000/api/users

修改 .webpackrc.js,加上 "proxy" 配置:

"proxy": {
  "/api": {
    "target": "http://jsonplaceholder.typicode.com/",
    "changeOrigin": true,
    "pathRewrite": { "^/api" : "" }
  }
},

然后启动应用:(这个命令一直开着,后面不需要重启)

$ npm start

浏览器会自动开启,并打开 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/fetchusers/save 的 action 以及相关的 state 。

Step 5. 添加界面,让用户列表展现出来

我们把组件存在 src/pages/users/components 里,所以在这里新建 Users.js 和 Users.css。具体参考这个 Commit

需留意两件事:

  1. 对 model 进行了微调,加入了 page 表示当前页
  2. 由于 components 和 services 中都用到了 pageSize,所以提取到 src/constants.js

改完后,切换到浏览器,应该能看到带分页的用户列表。

Step 6. 添加 layout

添加 layout 布局,使得我们可以在首页和用户列表页之间来回切换。umi 里约定 layouts/index.js 为全局路由,所以我们新增 src/layouts/index.js 和 CSS 文件即可。

参考这个 Commit

注意:

  1. 页头的菜单会随着页面切换变化,高亮显示当前页所在的菜单项

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 就好。

处理分页有两个思路:

  1. 发 action,请求新的分页数据,保存到 model,然后自动更新页面
  2. 切换路由 (由于之前监听了路由变化,所以后续的事情会自动处理)

我们用的是思路 2 的方式,好处是用户可以直接访问到 page 2 或其他页面。

参考这个 Commit

Step 9. 处理用户删除

经过前面的 8 步,应用的整体脉络已经清晰,相信大家已经对整体流程也有了一定了解。

后面的功能调整基本都可以按照以下三步进行:

  1. service
  2. model
  3. component

我们现在开始增加用户删除功能。

  1. service, 修改 src/pages/users/services/users.js
export function remove(id) {
  return request(`/api/users/${id}`, {
    method: 'DELETE',
  });
}
  1. 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 } });
},
  1. component, 修改 src/pages/users/components/Users.js,替换 deleteHandler 内容:
dispatch({
  type: 'users/remove',
  payload: id,
});

切换到浏览器,删除功能应该已经生效。

Step 10. 处理用户编辑

处理用户编辑和前面的一样,遵循三步走:

  1. service
  2. model
  3. 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 状态,有几种选择:

  1. 存 dva 的 model state 里
  2. 存 component state 里

另外,怎么存也是个问题,可以:

  1. 只有一个 visible,然后根据用户点选的 user 填不同的表单数据
  2. 几个 user 几个 visible

此教程选的方案是 2-2,即存 component state,并且 visible 按 user 存。另外为了使用的简便,封装了一个 UserModal 的组件。

完成后,切换到浏览器,应该就能对用户进行编辑了。

Step 11. 处理用户创建

相比用户编辑,用户创建更简单些,因为可以共用 UserModal 组件。和 Step 10 比较类似,就不累述了,详见 Commit


到这里,我们已经完成了一个完整的 CURD 应用。如果感兴趣,可以进一步看下 dva 和 umi 的资料:

(完)

@qppq54s
Copy link

qppq54s commented Feb 28, 2018

在windows10下,执行第一步

npm i dva-cli@next -g
dva -v
dva-cli version 1.0.0-bea.2

然后执行

dva new user-dashboard
cd user-dashboard
npm start

chrome 浏览器报错

Unhandled Rejection (TypeError): Cannot read property 'call' of undefined
▶ 2 stack frames were collapsed.
392
C:/Users/qiupe/Desktop/Code/user-dashboard/src/global.css:1
> 1 | exports = module.exports = require("../node_modules/css-loader/lib/css-base.js")(false);
  2 | // imports
  3 | 
  4 | 
View compiled
▼ 2 stack frames were expanded.
__webpack_require__
internal:/webpack/bootstrap d6d928234cf1607d4163:707
fn
internal:/webpack/bootstrap d6d928234cf1607d4163:112
▲ 2 stack frames were expanded.
391
./src/global.css?6274:4
  1 | // style-loader: Adds some css to the DOM by adding a <style> tag
  2 | 
  3 | // load the styles
> 4 | var content = require("!!../node_modules/css-loader/index.js??ref--6-1!../node_modules/postcss-loader/lib/index.js??postcss!./global.css");
  5 | if(typeof content === 'string') content = [[module.id, content, '']];
  6 | // Prepare cssTransformation
  7 | var transform;
View compiled
▶ 2 stack frames were collapsed.

@sorrycc
Copy link
Owner Author

sorrycc commented Feb 28, 2018

@qppq54s 这个问题偶现,遇到了刷新就好。

@LeesinChen
Copy link

你好,想问一下配置文件在哪里,项目需要些一点样式,怎么添加sass

@sorrycc
Copy link
Owner Author

sorrycc commented Feb 28, 2018

@JianmingXia
Copy link

JianmingXia commented Mar 1, 2018

项目中没有Step 2所说的.webpackrc.js文件,新建这个文件后,如下错误:

Watch handler failed, since .webpackrc file and .webpackrc.js file can not exist at the same time.

这两个文件是不是都会转成webpack的配置,只有后缀的区别?

更新一下,找到答案了https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md#%E9%85%8D%E7%BD%AE

@AndyLIUMel
Copy link

NPM start 后 弹出错误,
win10 64 bit
按照教程安装 dva-cli umi umi-plugin-dva

Failed to compile.

./src/pages/.umi/router.js
Syntax error: C:\Users\Andy\Documents\GitHub\user-dashboard\src\pages.umi\router.js: Bad character escape sequence (17:104)

15 |
16 |

17 | <Route exact path="/" component={() => React.createElement(require('C:\Users\Andy\Documents\GitHub\user-dashboard\node_modules\umi-build-dev\lib\Compiling.js').default, { route: '/' })} />
| ^
18 |
19 |
20 | );

@sorrycc
Copy link
Owner Author

sorrycc commented Mar 2, 2018

@AndyLIUMel try umi@1.0.3

@AndyLIUMel
Copy link

After update to umi@1.03,
MSG again
./src/pages/.umi/router.js
Syntax error: C:\Users\Andy\Documents\GitHub\user-dashboard\src\pages.umi\router.js: Bad character escape sequence (17:104)

15 |
16 |

17 | <Route exact path="/" component={() => React.createElement(require('C:\Users\Andy\Documents\GitHub\user-dashboard\node_modules\umi-build-dev\lib\Compiling.js').default, { route: '/' })} />
| ^
18 |
19 |
20 | );

@sorrycc
Copy link
Owner Author

sorrycc commented Mar 6, 2018

@lty6429 npm 依赖没装好吧,删除 node_modules 重装试试。

@lty6429
Copy link

lty6429 commented Mar 6, 2018

呃,前面问的问题是这个
按照教程启动是出错

'umi' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @ start: umi dev
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?

刚发出去就删除了,没想到作者速度这么快就响应了0.0

我现在是这样解决的,我去单独安装一下umi模块试试

npm i umi -g
如果不行我在去把node_modules 删除重装试试。。

@lty6429
Copy link

lty6429 commented Mar 7, 2018

'umi' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @ start: umi dev
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

删除 node_modules 重装之后还是没有解决这个问题,会是哪里出现了问题呢

@sorrycc
Copy link
Owner Author

sorrycc commented Mar 7, 2018

PATH 环境变量没配好?

@lty6429
Copy link

lty6429 commented Mar 7, 2018

node的环境变量如下

C:\Program Files\nodejs
应该只要配置到这里吧

@lty6429
Copy link

lty6429 commented Mar 7, 2018

这些指令都能用

node -v
v8.9.4
npm -v
5.6.0
应该不是环境变量的问题

@liuxj09
Copy link

liuxj09 commented Mar 9, 2018

IE10 访问首页,会报“SCRIPT5009: “Map”未定义 umi.js, 行24891 字符3,代码是下面这行:
var VALID_FRAGMENT_PROPS = new Map([['children', true], ['key', true]]);
我想问下,dva2没有自己做IE的兼容吗?

@sorrycc
Copy link
Owner Author

sorrycc commented Mar 9, 2018

@liuxj09 新建 src/global.js,在这里加 @babel/polyfill 试试。

@meteor-lsw
Copy link

delete

删除功能成功触发了delete请求,但数据好像并没有删掉。是不是因为代理的api服务器并没有真的在响应delete请求时删除数据

@sorrycc
Copy link
Owner Author

sorrycc commented Mar 11, 2018

删除功能成功触发了delete请求,但数据好像并没有删掉。是不是因为代理的api服务器并没有真的在响应delete请求时删除数据

是的。

@comll18
Copy link

comll18 commented Mar 12, 2018

Redux 开发者工具的配置方法能发一下吗?
tim 20180312153700

@xuqiang1227
Copy link

@liuxj09 请入在web容器中运行,比如nginx、apache等。

@feitingjun
Copy link

tim 20180312164834
请问rudex的开发者工具在框架里面需要怎么配置?

@sorrycc
Copy link
Owner Author

sorrycc commented Mar 12, 2018

@feitingjun 不用配置,装插件就好了。

@feitingjun
Copy link

装了插件了 然后提示 No store found. Make sure to follow the instructions.

@comll18
Copy link

comll18 commented Mar 12, 2018

约定式路由可以实现路由嵌套吗?另外,同问上面的问题,谢谢!
找不到初始化store的位置呢

11

@jizhuoling2018
Copy link

请问umi嵌套路由是如何实现的?

@sjqjane
Copy link

sjqjane commented Nov 9, 2018

项目中没有Step 2所说的.webpackrc.js文件,新建这个文件后,如下错误:

Watch handler failed, since .webpackrc file and .webpackrc.js file can not exist at the same time.

这两个文件是不是都会转成webpack的配置,只有后缀的区别?

更新一下,找到答案了https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md#%E9%85%8D%E7%BD%AE

没有看懂 能说下啥意思吗 没有webpackrc这个文件,新建并加入代码后报错了,新建.webpackrc.js还是.webpackrc有后缀吗

@life88
Copy link

life88 commented Nov 9, 2018

@sjqjane .webpackrc跟.webpackrc.js是同一个东西,只是配置的格式不一样前者json方式,后者js方式,取其一。

@xiaohuaxiangwoyiyang
Copy link

这个默认是localhost:8000,如何改成https呀?

@wei-gen
Copy link

wei-gen commented Dec 5, 2018

我在model里面根据上面的进行路由监听,但是显示却一直报错,输出dispatch是未定义的
default
2

@wallowbear
Copy link

Step 2. 配置代理,能通过 RESTFul 的方式访问 http://localhost:8000/api/users

修改 .webpackrc.js,加上 "proxy" 配置:

"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
},

我照着做到step2,没有生成webpackrc.js啊。。。。?

@lfl-karl-el
Copy link

@sorrycc 这个部署到tomcat怎么去访问,后台部署在另一台服务器的tomcat,报404错误

@panw3i
Copy link

panw3i commented Dec 12, 2018

能讲清楚一吗? 两个都 能生成脚手架,到底是用哪个?

@gbjuno
Copy link

gbjuno commented Dec 30, 2018

Step 2. 配置代理,能通过 RESTFul 的方式访问 http://localhost:8000/api/users

修改 .webpackrc.js,加上 "proxy" 配置:

"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
},

我照着做到step2,没有生成webpackrc.js啊。。。。?

新版本的脚手架用的umi版本改为使用.umirc.js了

export default {
  plugins: ['umi-plugin-dva'],
  "proxy": {
    "/api": {
      "target": "http://jsonplaceholder.typicode.com/",
      "changeOrigin": true,
      "pathRewrite": { "^/api": "" }
    }
  }
}

@miaozilong
Copy link

期待出新版出来

@LianCui
Copy link

LianCui commented Feb 21, 2019

你好 这个存在兼容问题,在ie浏览器上打不开,请问怎样解决这个兼容

@JefferyHo
Copy link

啥时候出umijs2版的教程啊?

这里有一个 https://www.codercto.com/a/25627.html

@miaozilong
Copy link

啥时候出umijs2版的教程啊?

这里有一个 https://www.codercto.com/a/25627.html

不错不错

@Laumlin
Copy link

Laumlin commented Apr 18, 2019

点击Ok按钮时报错:props中存在onOk属性,但是执行时报错,求解答
image

image

@stephenzhao
Copy link

我有个疑惑:
dva 如何处理局部数据的loading和error的。我现在看到的是由一个插件去处理全局的报错,以及loading。但是局部的情况怎么处理呢?
比如:userList:[{id: 0, name: 'a'},{id:11, name: 'b'}],这个列表展示出来后,列表每一项还会去请求一个信息项,比如积分,那必然会并发dispatch一些effect去取积分数据,这个积分数据的loading状态怎么维护,某一项发生错误,我需要在局部标明这个错误,这个error怎么维护。
请问有什么最佳实践吗?

@MakerGYT
Copy link

点击edit,
index.js:2177 Warning: Received `true` for a non-boolean attribute `horizontal`.

@1232209
Copy link

1232209 commented Jun 27, 2019

约定式路由可以实现路由嵌套吗?另外,同问上面的问题,谢谢!
找不到初始化store的位置呢

11

安装redux-devtools,是需要配置一下的,可是找半天也没找到store是在哪里创建的

@1232209
Copy link

1232209 commented Jun 27, 2019

用户管理项目运行起来有报错:
图片
在浏览器中多点几次必然会报这个代理的错误,查看网络发现状态码是504。
图片
不知道这个错误发生的原因是后台出错返回状态码504引起的程序崩溃,还是根本就没有把请求发出去,就是本地代理出错直接引起程序崩溃?希望和我碰到相同问题的朋友能帮忙看看。

@1232209
Copy link

1232209 commented Jun 27, 2019

约定式路由可以实现路由嵌套吗?另外,同问上面的问题,谢谢!
找不到初始化store的位置呢
11

安装redux-devtools,是需要配置一下的,可是找半天也没找到store是在哪里创建的

dva-cli1.00beta4创建的项目redux-devtools无法找到store,但是umi@2创建的项目已经有了

@1232209
Copy link

1232209 commented Jun 27, 2019

约定式路由可以实现路由嵌套吗?另外,同问上面的问题,谢谢!
找不到初始化store的位置呢
11

安装redux-devtools,是需要配置一下的,可是找半天也没找到store是在哪里创建的

dva-cli1.00beta4创建的项目redux-devtools无法找到store,但是umi@2创建的项目已经有了

有点搞不清楚到底该用哪个脚手架了,在官方推荐的umi with dva项目(user-dashboard)中,dva-cli 1.0.0beta4创建的项目目录结构components,models,services文件夹根本就没有用到,全是在pages文件夹就搞定了,是啥意思呢?

@xgqfrms

This comment has been minimized.

@liran
Copy link

liran commented Sep 19, 2019

什么鬼教程,完全看不懂;

到底是 dva 的项目,还是 umi 的项目,结构目录完全不同?

image

  1. 为什么不提供一个单独的 demo repo?
  2. demo 要保持更新与 dva-cli 一致;
  3. issues 里面是提问题的,不是做 wiki 的;
  4. 官方的文档也不清楚,kpi 产物;

image

2 年前的老项目,也不更新

image

老项目老教程不是很搭吗?前端更新速度很快,现在再去学习这些比较早的项目问题当然会不少,而且被支持解决的可能性很低。
推荐从这里开始:https://umijs.org/guide/create-umi-app.html#introduce-create-umi
至于到底是umi还是dva项目可以看这里:https://umijs.org/guide/with-dva.html#faq
说白了dva现在已经变成umi的底层依赖,目录约定得按umi的来。

@sandersyao
Copy link

希望能有应用 GraphQL 的例子

@llh1187
Copy link

llh1187 commented Jan 5, 2020

请问mock数据怎么配呢?直接在mock文件夹下写的js文件无法读取出来的。用postman也不行。

@ououwang
Copy link

ououwang commented Mar 9, 2020

nice

@aspirantzhang
Copy link

UMI3版本:
本人录制了umi+dva初学者视频,已在B站上线,面向dva和umi初学者,实现一个用户管理的CRUD列表,详见:
Umi 3+Dva入门实践 完成用户管理的CRUD应用(2020)

@zwtcici
Copy link

zwtcici commented May 20, 2020

创建和删除都不起作用?是怎么回事

@yongjiangliu
Copy link

image
这个文件都没看到,按照文件创建项目之后。。。。。。
image

@affaanmansoori
Copy link

For people confused with Directory structure and How to create a UMI project with DVA support use the link to first create a starting project - https://v2.umijs.org/guide/create-umi-app.html#introduce-create-umi Then follow the tutorial :)

image

@jayguojianhai
Copy link

jayguojianhai commented Dec 8, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests