dva 是對(duì) redux 的一層淺封裝,基于 react 語(yǔ)言,實(shí)現(xiàn)前端代碼的分層
一般會(huì)分為3層:
- component:組件渲染、展示頁(yè)面
- models:數(shù)據(jù)組裝、處理
- services:接口調(diào)用、拿數(shù)據(jù)
以 smart-ui 項(xiàng)目中用戶列表模塊為例:

smart-ui
1. router.js 文件中配置路由,指定具體路徑所要加載的 views 和 models
{
path: '/security/users',
models: () => [import('./models/security/user')],
component: () => import('./views/security/user/'),
}
2. services
.roadhogrc.js文件中配置好代理請(qǐng)求地址
proxy: {
"/api/v1/weather": {
"target": "https://api.seniverse.com/",
"changeOrigin": true,
"pathRewrite": { "^/api/v1/weather" : "/v3/weather" }
},
"/services": {
"target": "http://ip地址:8080/",
"changeOrigin": true,
"pathRewrite": { "^/services" : "/services" }
},
},
serviecs/user.js中定義好接口,獲取原始數(shù)據(jù)。項(xiàng)目中,已對(duì)常用的增刪改查做了一層封裝(包括分頁(yè)).
對(duì)于自定義方法,提供 url、method、data(可選),返回 request 封裝好的 http 請(qǐng)求
export async function query({page = 1 , pageSize = 10 , ...qs}) {
return user.listPage(page,pageSize,qs)
}
export async function get({id}) {
return user.get(id)
}
export async function create (params) {
return user.create(params);
}
export async function update (params) {
return user.update(params);
}
// 用戶設(shè)置 自定義方法
//修改用戶
export async function modify(payload) {
return request({
url: `${apiPrefix}/security/user/modify`,
method: 'put',
data: payload
})
}
3. models
import pathToRegexp from 'path-to-regexp'
import modelExtend from 'dva-model-extend'
import * as user from 'services/security/user'
import { pageModel } from 'models/common'
import { message } from 'antd'
export default modelExtend(pageModel, {
namespace: 'secUser',
state: {
currentItem: {},
modalVisible: false,
modalType: 'create',
selectedRowKeys: [],
},
// 添加一個(gè)監(jiān)聽(tīng)器,當(dāng)pathname === '/security/users'時(shí)執(zhí)行dispatch
subscriptions: {
setup ({ dispatch, history }) {
history.listen((location) => {
if (location.pathname === '/security/users') {
dispatch({
type: 'query',
payload: location.query || {},
})
}
})
},
},
//
effects: {
* query ({
payload,
}, { call, put }) {
const result = yield call(user.query, payload)
const { success, message, status, data } = result
if (success) {
yield put({
type: 'querySuccess',
payload: data,
})
} else {
throw result
}
},
* create ({ payload }, { call, put,select }) {
const {data} = yield call(user.create, payload)
message.info("新增成功");
yield put({ type: 'hideModal' })
const { pagination: { pageSize,current } } = yield select(_ => _.secUser)
yield put({ type: 'query', payload: {pageSize, page:current } })
},
* update ({ payload }, { select, call, put }) {
const id = yield select(({ secUser }) => secUser.currentItem.id)
const newUser = { ...payload, id }
const {data} = yield call(user.update, newUser)
message.info("修改成功");
yield put({ type: 'hideModal' })
const { pagination: { pageSize,current } } = yield select(_ => _.secUser)
yield put({ type: 'query', payload: {pageSize, page:current } })
},
},
reducers: {
showModal (state, { payload }) {
return { ...state, ...payload, modalVisible: true }
},
hideModal (state) {
return { ...state, modalVisible: false }
},
},
})
- query、update 前面的 * 號(hào),表示這個(gè)方法是一個(gè) Generator函數(shù)
- subscriptions:用于添加一個(gè)監(jiān)聽(tīng)器
- effects:異步執(zhí)行 dispatch,用于發(fā)起異步請(qǐng)求
- call 是調(diào)用執(zhí)行一個(gè)函數(shù) (調(diào)用service里面的方法)
- put 則是相當(dāng)于 dispatch 執(zhí)行一個(gè) action
- select 則可以用來(lái)訪問(wèn)其它 model
- reducers:同步請(qǐng)求,主要用來(lái)改變state
4.component
注意:組件入口文件一定要命名為 index.js ,否則會(huì)找不到
import React from 'react'
import PropTypes from 'prop-types'
import { routerRedux } from 'dva/router'
import { connect } from 'dva'
import { Row, Col, Button, Popconfirm } from 'antd'
import List from './List'
import Filter from './Filter'
import Modal from './Modal'
import { Page } from 'components'
import { i18n } from 'utils'
const User = ({ location, dispatch, secUser, loading }) => {
const { dataSource, pagination, currentItem, modalVisible, modalType, selectedRowKeys } = secUser
const { pageSize } = 10
const modalProps = {
item: modalType === 'create' ? {} : currentItem,
visible: modalVisible,
maskClosable: false,
confirmLoading: loading.effects['secUser/update'],
title: modalType === 'create' ? i18n('lab.user.create') : i18n('lab.user.update'),
wrapClassName: 'vertical-center-modal',
onOk (data) {
dispatch({
type: `secUser/${modalType}`,
payload: data,
})
},
onCancel () {
dispatch({
type: 'secUser/hideModal',
})
},
}
const listProps = {
dataSource,
loading: loading.effects['secUser/query'],
pagination,
location,
onChange (page) {
const { query, pathname } = location
dispatch(routerRedux.push({
pathname,
query: {
...query,
page: page.current,
pageSize: page.pageSize,
},
}))
},
onDeleteItem (id) {
dispatch({
type: 'secUser/delete',
payload: id,
})
},
onEditItem (item) {
dispatch({
type: 'secUser/showModal',
payload: {
modalType: 'update',
currentItem: item,
},
})
},
rowSelection: {
selectedRowKeys,
onChange: (keys) => {
dispatch({
type: 'secUser/updateState',
payload: {
selectedRowKeys: keys,
},
})
},
},
}
const filterProps = {
...
}
const handleDeleteItems = () => {
dispatch({
type: 'secUser/multiDelete',
payload: {
ids: selectedRowKeys,
},
})
}
return (
<Page inner>
<Filter {...filterProps} />
<List {...listProps} />
{modalVisible && <Modal {...modalProps} />}
</Page>
)
}
User.propTypes = {
secUser: PropTypes.object,
location: PropTypes.object,
dispatch: PropTypes.func,
loading: PropTypes.object,
}
export default connect(({ secUser }) => ({ secUser}))(User)
- connect方法將組件與數(shù)據(jù)關(guān)聯(lián)在一起
export default connect(({ secUser }) => ({ secUser}))(User) - secUser 必須與對(duì)應(yīng) models 里面定義的 namespace 字段一致
- 組件中發(fā)起請(qǐng)求調(diào)用 dispatch
- type 字段對(duì)應(yīng) models 中 effects 和 reducers 對(duì)應(yīng)方法
- payload 為傳參對(duì)象