前言
dva 官方项目里面有些用法不是最新的,本文针对性的解决并以正确的用法告知刚开始接触dva 项目的码友们 ,帮助大家避免入坑。
12 步 30 分钟,完成用户管理的 CURD 应用 优化版
git地址:https://github.com/Sawyer-china/react-user-dashboard
现在我们就开始一步一步的构建 如遇见问题可 添加 qq群:218618405 进行提问
如果你觉得该文章对你有帮助加个喜欢,github 加个 start 谢谢
1. 安装全局 dva-cli
$ npm install dva-cli -g
2. 创建项目
选好目录然后创建项目
$ dva new user-dashboard
3. 根据提示进入目录并运行项目
$ cd user-dashboard
$ npm start
如果看到以下页面,那么恭喜你,我们往下进行了
image.png
4. 引入antd组件库 (andt: 官方地址 https://ant.design/index-cn)
$ npm install antd --save
安装完成后打开 src/routes/IndexPage.js 引入一个antd组件试试 在文件头部引入
import Button from 'antd/lib/button'
import 'antd/lib/button/style/css';
在function IndexPage 函数中使用组件
<Button type="primary">Primary</Button>
如果页面出现一个Button 则代表成功了
5. 按需加载
在项目中往往我们需要进行组件的按需加载以免去不必要的组件被打包到实际的项目中,以减少js的体积大小
安装以下插件
$ cnpm install babel-plugin-import --save-dev
并找到根目录下面的.webpackrc文件,并在文件中添加插件配置
"extraBabelPlugins": [
["import", { "libraryName": "antd", "style": "css" }]
]
配置更多玩法参考:https://github.com/sorrycc/roadhog/blob/master/README_zh-cn.md
修改以下代码
// import Button from 'antd/lib/button'
// import 'antd/lib/button/style/css';
import { Button } from 'antd'
6. 做 webpack 反向代理
在配置文件中添加以下代码
"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api": "" }
}
}
访问 http://localhost:8000/api/users 如果你看见一串json数据代表代理成功,就可以进行下一步开发了(该json数据是dva官方提供的测试数据,使用Mockjs开发)
完成以上准备工作我们就开始正式的demo开发了
7. 创建 Users.js Router
在routes目录下创建Users.js
import React, { Component } from 'react'
import { connect } from 'dva'
import styles from './Users.css'
class Users extends Component {
render() {
return (
<div className={styles.normal}>
Users.js
</div>
)
}
}
Users.propsTypes = {}
export default connect()(Users)
8. 配置路由 打开根目录router.js
import React from 'react'
import { Router, Route, Switch, Redirect, routerRedux } from 'dva/router'
import IndexPage from './routes/IndexPage'
import dynamic from 'dva/dynamic' // 路由按需加载
const { ConnectedRouter } = routerRedux
function RouterConfig({ history, app }) {
const IndexPage = dynamic({
app,
component: () => import('./routes/IndexPage')
})
const Users = dynamic({
app,
component: () => import('./routes/Users')
})
return (
<ConnectedRouter history={history}>
<Switch>
<Route path="/" exact component={IndexPage} />
<Route path="/users" exact component={Users} />
</Switch>
</ConnectedRouter>
)
}
export default RouterConfig
浏览地址 输入 http://localhost:8000/#/users 将会看到 users 路由页面
9. 在components 文件夹下 新建 MainLayout/Header.js
import React, { Component } from 'react'
import { Menu, Icon } from 'antd'
import { connect } from 'dva'
import { Link, routerRedux } from 'dva/router'
class Header extends Component {
render() {
const { location } = this.props
return (
<Menu
selectedKeys={[location.pathname]}
mode="horizontal"
theme="dark"
>
<Menu.Item key="/users">
<Link to="/users">
<Icon type="bars" />Users
</Link>
</Menu.Item>
<Menu.Item key="/">
<Link to="/">
<Icon type="home" />Home
</Link>
</Menu.Item>
<Menu.Item key="/404">
<Link to="/page-you-dont-know">
<Icon type="frown-circle" />404
</Link>
</Menu.Item>
<Menu.Item key="/antd">
<a href="https://github.com/dvajs/dva">dva</a>
</Menu.Item>
</Menu>
)
}
}
export default connect()(Header)
10. 在 components/MainLayout 新建MainLayout.js
import React, { Component } from 'react'
import styles from './MainLayout.css'
import Header from './Header'
class MainLayout extends Component {
render() {
const { children, location } = this.props
return (
<div className={styles.normal}>
<Header />
<div className={styles.content}>
<div className={styles.main}>
{children}
</div>
</div>
</div>
)
}
}
export default MainLayout
11. 在 routes 中添加 App.js
import React, { Component } from 'react'
import { connect } from 'dva'
import { withRouter } from 'dva/router'
import MainLayout from '../components/MainLayout/MainLayout'
class App extends Component {
render() {
let { children, location } = this.props
return (
<MainLayout location={location}>
{children}
</MainLayout>
)
}
}
App.propTypes = {}
export default withRouter(
connect(({ app, loading }) => ({
app,
loading
}))(App)
)
添加完成之后修改 router.js 页面
在头部引入 App.js
import App from './routes/App'
然后修改return 中的代码
return (
<ConnectedRouter history={history}>
<App>
<Switch>
<Route path="/" exact component={IndexPage} />
<Route path="/users" exact component={Users} />
<Route path="*" render={() => <Redirect to="users" />} />
</Switch>
</App>
</ConnectedRouter>
)
现在就可以切换路由了如下图示:
222.gif
接下来着重users页面的开发
12. 创建 users model 和 service
新建 src/models/users.js
:
// user api
import * as usersService from '../services/users'
// 引入 node 模块
// import url from 'url'
// import qs from 'qs'
export default {
namespace: 'users',
state: {
list: [],
total: 0,
page: 0
},
reducers: {
/**
* test
* @param {*} state
* @param {*} param1
*/
save(state, { payload: { data: list, total, page } }) {
return { ...state, list, total, page }
},
search(state) {
return { ...state }
}
},
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'],
page: parseInt(page, 10)
}
})
},
*create({ payload: values }, { call, put }) {
yield call(usersService.create, values)
},
*patch({ payload: { id, values } }, { call, put }) {
yield call(usersService.patch, { id, values })
yield put({ type: 'reload' })
},
*remove({ payload: { id } }, { call, put }) {
yield call(usersService.remove, { id })
yield put({ type: 'reload' })
},
*reload(action, { put, select }) {
const page = yield select(state => state.users.page)
yield put({ type: 'fetch', payload: { page } })
}
},
subscriptions: {
// setup({ dispatch }, done) {
// done('错了错了')
// throw new Error('Whoops!')
// }
setup({ dispatch, history }) {
return history.listen(({ pathname, search }) => {
// const { query } = url.parse(search)
// const oPath = qs.parse(query)
// if (pathname === '/users') {
// console.log('/users')
// console.log(oPath)
// dispatch({ type: 'fetch', payload: oPath })
// }
})
}
}
}
新建 src/services/users.js
import request from '../utils/request'
export function queryUsers() {
return request('/api/users')
}
export function fetch({ page = 1 }) {
return request(`/api/users?_page=${page}&_limit=5`)
}
export function create(values) {
return request('/api/users', {
methods: 'POST',
data: JSON.stringify(values)
})
}
export function patch({ id, values }) {
return request(`/api/users/${id}`, {
methods: 'PATCH',
data: JSON.stringify(values)
})
}
export function remove({ id }) {
return request(`/api/users/${id}`, {
methods: 'DELETE'
})
}
由于我们需要从 response headers 中获取 total users 数量,所以需要改造下 src/utils/request.js:
import fetch from 'dva/fetch'
// function parseJSON(response) {
// return response.json()
// }
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 function request(url, options) {
// return fetch(url, options)
// .then(checkStatus)
// .then(parseJSON)
// .then(data => ({ data }))
// .catch(err => ({ err }));
// }
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
}
export default request
剩余部分请参考
https://github.com/sorrycc/blog/issues/18 (该代码可能有部分内容会导致错误)
请以一下链接为准
https://github.com/Sawyer-china/react-user-dashboard
最终完成效果如下图所示:
如遇问题欢迎加群讨论
QQ群: 218618405
github:https://github.com/Sawyer-china
网友评论
app.router(({history, app}) =>
<Router history={history}>
<Route path="/" component={HomePage} />
</Router>
);
Module not found: Can't resolve 'dva/dynamic' 是什麽情況?