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

12 步 30 分钟,完成用户管理的 CURD 应用 (react+dva+antd) #18

Open
sorrycc opened this issue Dec 21, 2016 · 357 comments
Open

12 步 30 分钟,完成用户管理的 CURD 应用 (react+dva+antd) #18

sorrycc opened this issue Dec 21, 2016 · 357 comments
Labels

Comments

@sorrycc
Copy link
Owner

sorrycc commented Dec 21, 2016

本文仅适用于 dva@1,dva@2 的文档请移步 #62
本文仅适用于 dva@1,dva@2 的文档请移步 #62
本文仅适用于 dva@1,dva@2 的文档请移步 #62

本文会一步步引导大家如何创建一个 CURD 应用,包含查询、编辑、删除、创建,以及分页处理,数据 mock,自动处理 loading 状态等,基于 react, dvaantd

最终效果:

开始之前:

  • 确保 node 版本是 6.5 +
  • cnpmyarn 能节约你安装依赖的时间

Step 1. 安装 dva-cli 并创建应用

先安装 dva-cli,并确保版本是 0.7.x。

$ npm i dva-cli@0.7 -g
$ dva -v
0.7.0

然后创建应用:

$ dva new user-dashboard
$ cd user-dashboard 

Step 2. 配置 antdbabel-plugin-import

babel-plugin-import 用于按需引入 antd 的 JavaScript 和 CSS,这样打包出来的文件不至于太大。

$ npm i antd --save
$ npm i babel-plugin-import --save-dev

修改 .roadhogrc,在 "extraBabelPlugins" 里加上:

["import", { "libraryName": "antd", "style": "css" }]

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

修改 .roadhogrc,加上 "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 4. 生成 users 路由

用 dva-cli 生成路由:

$ dva g route users

然后访问 http://localhost:8000/#/users

Step 5. 构造 users model 和 service

用 dva-cli 生成 Model :

$ dva g model users

修改 src/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/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 6. 添加界面,让用户列表展现出来

用 dva-cli 生成 component:

$ dva g component Users/Users

然后修改生成出来的 src/components/Users/Users.jssrc/components/Users/Users.css,并在 src/routes/Users.js 中引用他。具体参考这个 Commit

需留意两件事:

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

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

Step 7. 添加 layout

添加 layout 布局,使得我们可以在首页和用户列表页之间来回切换。

  1. 添加布局,src/components/MainLayout/MainLayout.js 和 CSS 文件
  2. src/routes 文件夹下的文件中引用这个布局

参考这个 Commit

注意:

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

Step 8. 通过 dva-loading 处理 loading 状态

dva 有一个管理 effects 执行的 hook,并基于此封装了 dva-loading 插件。通过这个插件,我们可以不必一遍遍地写 showLoading 和 hideLoading,当发起请求时,插件会自动设置数据里的 loading 状态为 true 或 false 。然后我们在渲染 components 时绑定并根据这个数据进行渲染。

先安装 dva-loading :

$ npm i dva-loading --save

修改 src/index.js 加载插件,在合适的地方加入下面两句:

+ import createLoading from 'dva-loading';
+ app.use(createLoading());

然后在 src/components/Users/Users.js 里绑定 loading 数据:

+ loading: state.loading.models.users,

具体参考这个 Commit

切换到浏览器,你的用户列表有 loading 了没?

Step 9. 处理分页

只改一个文件 src/components/Users/Users.js 就好。

处理分页有两个思路:

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

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

参考这个 Commit

Step 10. 处理用户删除

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

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

  1. service
  2. model
  3. component

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

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

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

Step 11. 处理用户编辑

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

  1. service
  2. model
  3. component

先是 service,修改 src/services/users.js

export function patch(id, values) {
  return request(`/api/users/${id}`, {
    method: 'PATCH',
    body: JSON.stringify(values),
  });
}

再是 model,修改 src/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 12. 处理用户创建

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


到这里,我们已经完成了一个完整的 CURD 应用。但仅仅是完成,并不完善,比如:

  • 如何处理错误,比如请求等
  • 如何处理请求超时
  • 如何根据路由动态加载 JS 和 CSS
  • ...

请期待下一篇。

(完)

@sorrycc sorrycc added the DvaJS label Dec 21, 2016
@sorrycc sorrycc changed the title 12 步 30 分钟,完成用户管理的 CURD 应用 (React+Dva+Antd) 12 步 30 分钟,完成用户管理的 CURD 应用 (react+dva+antd) Dec 21, 2016
@JimmyLv
Copy link

JimmyLv commented Dec 21, 2016

简直好用到哭,😂

@Ygg-w
Copy link

Ygg-w commented Dec 22, 2016

这就是我想要的东西~👍👍👍

@Ygg-w
Copy link

Ygg-w commented Dec 22, 2016

期待下一篇~

@maying2020
Copy link

dispatch(routerRedux.push({
pathname: '/users',
query: { page },
}));

routerRedux 可以直接切换路由?
如果是这样的话 也太方便的吧 之前是从最终的组件一直disptach行为

@sorrycc
Copy link
Owner Author

sorrycc commented Dec 23, 2016

routerRedux.push() 返回的是 action,然后把他让 dispatch 调用下就可以切换路由了。

@uwang
Copy link

uwang commented Dec 23, 2016

👍 最近文档更新的越来越易读了

@linyongping
Copy link

期待新作,,,

@linyongping
Copy link

linyongping commented Dec 24, 2016

现在的项目在用是 dva 0.60,,可以直接升级到0.70吗

@sorrycc
Copy link
Owner Author

sorrycc commented Dec 24, 2016

@linyongping dva-cli@0.6 ? 到 0.7 主要是工具的变动,要换到 roadhog,可以参考 #17

@lujiabz
Copy link

lujiabz commented Dec 25, 2016

image
请问要支持await还需要配置什么吗

@sorrycc
Copy link
Owner Author

sorrycc commented Dec 25, 2016

@gitoneman await 必须在 async 函数里。

@lujiabz
Copy link

lujiabz commented Dec 25, 2016

@sorrycc 谢谢,对await不熟悉

@kernelogic
Copy link

请问npm start出现这个错误

`npm start

@ start /Users/feiya200/Documents/user-dashboard
roadhog server

/Users/feiya200/Documents/user-dashboard/node_modules/roadhog/bin/roadhog.js:9
const [major, minor] = nodeVersion.split('.');
^

SyntaxError: Unexpected token [
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:373:25)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Function.Module.runMain (module.js:441:10)
at startup (node.js:139:18)
at node.js:990:3

npm ERR! Darwin 15.6.0
npm ERR! argv "/usr/local/Cellar/node@4/4.7.0/bin/node" "/usr/local/bin/npm" "start"
npm ERR! node v4.7.0
npm ERR! npm v2.15.9
npm ERR! code ELIFECYCLE
npm ERR! @ start: roadhog server
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ start script 'roadhog server'.
npm ERR! This is most likely a problem with the package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! roadhog server
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!
npm ERR! npm owner ls
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR! /Users/feiya200/Documents/user-dashboard/npm-debug.log
`

@sorrycc
Copy link
Owner Author

sorrycc commented Dec 27, 2016

node 版本要求 6.5 或以上。

@kernelogic
Copy link

oops

@llqgit
Copy link

llqgit commented Dec 28, 2016

请问,我想配置端口怎么设置?因为本地想开多个 dva 项目,但是改不了端口。
npm start 改成这个了:

"start": "dora --plugins \"proxy?port=8089&watchDirs=./mock,webpack,webpack-hmr\""

但是还有个 8000 端口

...
          proxy: listened on 8089
Caught exception: Error: listen EADDRINUSE :::8000
...

@sorrycc

@sorrycc
Copy link
Owner Author

sorrycc commented Dec 28, 2016

@llqgit 参考 #17 切换到 roadhog,然后 PORT=8001 npm start 就好了。

@llqgit
Copy link

llqgit commented Dec 28, 2016

@sorrycc 好,谢谢

@MelissaHu
Copy link

报错,npm start之后~~~
Failed to compile.
Error in ./src/index.js
Module build failed: Error: Plugin 3 specified in "base" provided an invalid property of "libraryName"
at Array.map (native)
@ multi index

@sorrycc
Copy link
Owner Author

sorrycc commented Dec 28, 2016

@MelissaHu 注意格式,参考 https://github.com/dvajs/dva-example-user-dashboard/blob/d6da33b/.roadhogrc#L20-L23

@TFous
Copy link

TFous commented Dec 29, 2016

想问下,为什么打包之后页面不能正常访问了。

@sorrycc
Copy link
Owner Author

sorrycc commented Dec 29, 2016

@TFous 如果是用的 browserHistory,打包后需要放根目录路由才会匹配到。

@leduold
Copy link

leduold commented Dec 29, 2016

payload的传参不知道到怎么在model里面获取到的。

@kwzm
Copy link

kwzm commented May 29, 2018

您好,我想问下,我用dva-cli@0.9.2创建项目,没有找到.roadhogrc,是不是新版本移除了这个配置文件呢,那要配置antd组件按需加载去哪配置呢?

@joriewong
Copy link

joriewong commented May 29, 2018

@kwzm .webpackrc

"extraBabelPlugins": [
    ["import", { "libraryName": "antd", "style": true }]
  ]

@Mojianshou
Copy link

step 4的时候提示了这个 cry dva generate is disabled since we don't have enough time currently. 请问这个是什么意思?谢谢大牛

@russellzhd
Copy link

russellzhd commented Jun 11, 2018

这个如何指定默认的路由跳转,就是npm start后指定跳转其他的路由而不是'/'路由 @sorrycc

@joriewong
Copy link

请问如果是离线react、react-dom要怎么引入呢?

@joriewong
Copy link

<script src="https://unpkg.com/react@16.0.0/umd/react.production.min.js"></script>
<script>!window.React && document.write("<script src=\"react.production.min.js\">" + "<\/script>")</script>
<script src="https://unpkg.com/react-dom@16.0.0/umd/react-dom.production.min.js"></script>
<script>!window.ReactDOM && document.write("<script src=\"react-dom.production.min.js\">" + "<\/script>")</script>

@wongmeng
Copy link

谢谢 分享!您的分享对我的学习很有帮助!

@wzono
Copy link

wzono commented Jul 17, 2018

path: "models/users"
error: " -> subscriptions -> history.listen -> |query|" query 不存在,导致page是undefined

@joriewong
Copy link

@wingsico

subscriptions: {
  setup({ dispatch, history }) {
      return history.listen(({ pathname, search }) => {
        const query = queryString.parse(search);
        if (pathname === '/users') {
          dispatch({ type: 'fetch', payload: query });
        }
      });
    },
  },

@hahaha12356
Copy link

你好··初次使用不太懂··我生成的项目为什么没有.roadhogrc 而是 .webpackrc 呢··这两个打包工具有冲突不能共存吗?求指点·

@paddingC
Copy link

paddingC commented Sep 7, 2018

image
这个是什么写法呢?文档里好像没有

@chenlong890223
Copy link

image
跟着一步步完成了 结果是这样

@LianCui
Copy link

LianCui commented Dec 27, 2018

image
我在下载antd的时候,会报这个错?为什么?是我的node的版本太低吗?

@Duanruilong
Copy link

Duanruilong commented Dec 29, 2018

~ drl_dva npm start

drl_dva@1.0.0 start /Users/xmaczone/DRL_Github/drl_dva
roadhog server

module.js:549
throw err;
^

Error: Cannot find module '../lib/server'
at Function.Module._resolveFilename (module.js:547:15)
at Function.resolve (internal/module.js:18:19)
at Object. (/Users/xmaczone/DRL_Github/drl_dva/node_modules/roadhog/bin/roadhog.js:41:16)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10)
at startup (bootstrap_node.js:188:16)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! drl_dva@1.0.0 start: roadhog server
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the drl_dva@1.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/xmaczone/.npm/_logs/2018-12-29T09_03_48_983Z-debug.log

启动项目报Cannot find module '../lib/server' 重新安装node_modules也没有用,求解 @sorrycc

@Duanruilong
Copy link

好高兴的准备npm start

BUT ====>>>>>> 报错了!!!还一直解决不了!!!
Error: Cannot find module '../lib/server'
😒😑😑

@AryaRicky
Copy link

膩害,感謝樓主

@l-ichaochao
Copy link

请问当点击按钮时有弹出框,这个怎么完成的,代码怎么没有找到啊

@aliantony
Copy link

npm start之后页面报如下错误,请问为什么?
./src/index.js
Module build failed: Error: You gave us a visitor for the node type "JSXOpeningFragment" but it's not a valid type
at Array.map ()

@th-tj
Copy link

th-tj commented Jan 28, 2019

npm start之后页面报如下错误,请问为什么?
./src/index.js
Module build failed: Error: You gave us a visitor for the node type "JSXOpeningFragment" but it's not a valid type
at Array.map ()

今天也遇到这个问题了,解决了吗?

@TerryZvk
Copy link

npm start之后页面报如下错误,请问为什么?
./src/index.js
Module build failed: Error: You gave us a visitor for the node type "JSXOpeningFragment" but it's not a valid type
at Array.map ()

我也遇到了这个问题,cnpm的锅,改用npm后啥事也没有

@TerryZvk
Copy link

npm start之后页面报如下错误,请问为什么?
./src/index.js
Module build failed: Error: You gave us a visitor for the node type "JSXOpeningFragment" but it's not a valid type
at Array.map ()

今天也遇到这个问题了,解决了吗?

用npm

@aliantony
Copy link

npm start之后页面报如下错误,请问为什么?
./src/index.js
Module build failed: Error: You gave us a visitor for the node type "JSXOpeningFragment" but it's not a valid type
at Array.map ()

我也遇到了这个问题,cnpm的锅,改用npm后啥事也没有

谢谢朋友

@673523000
Copy link

走到第2步走不下去了,没找到 .roadhogrc 文件,但是有个.roadhogrc.mock.js 文件,求教怎么往下进行,dva-cli version 0.10.0 ;

@TerryNanshan2
Copy link

走到第2步走不下去了,没找到 .roadhogrc 文件,但是有个.roadhogrc.mock.js 文件,求教怎么往下进行,dva-cli version 0.10.0 ;

dva 新版,直接在.webpackrc文件中修改就行,注意格式

@TerryNanshan2
Copy link

step 4的时候提示了这个 cry dva generate is disabled since we don't have enough time currently. 请问这个是什么意思?谢谢大牛

我今天也遇到这个问题了,原来是作者没时间,所以此dva generate功能未开放

@790557934
Copy link

dva在做动态加载路由 用到了dynamic 但是报错,怎么解决?
image

@feidudu1
Copy link

feidudu1 commented Jul 9, 2019

刷新该页面,加载样式缓慢,在加载这个页面的时候,最开始出来的html是没有antd样式的(肉眼可见),然后才呈现了最终样式。
如果从其他页面跳转过来肉眼看不到没有antd样式的html这个过程
image
是哪里没配好还是需要优化呢

@feidudu1
Copy link

feidudu1 commented Jul 9, 2019

step 4的时候提示了这个 cry dva generate is disabled since we don't have enough time currently. 请问这个是什么意思?谢谢大牛

我今天也遇到这个问题了,原来是作者没时间,所以此dva generate功能未开放

你没用他指定的版本,新版的是不支持的

@easyTom
Copy link

easyTom commented Oct 9, 2019

想问下 state这种刷新页面就重置,假如做一些业务是要在api连接后台 存入数据库 或者用localStorage这种吗?

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