$ npm install dva-cli -g
$ dva new dva-demo
$ cd dva-demo
$ npm start
assets:媒体文件,存放图片等。
components:最基础的组件。存放基本的UI组件,这些组件接收外部传过来的参数(数据),并将这些数据渲染到界面。根据传入参数的不同,界面渲染也不同。
models:model是dva中的一个重要概念,可以看作数据层。
应用的state被存储在一个object tree中,应用的初始state在model中定义,也就是说,由model state组成全局state。
dva将model以namespace作为唯一标识进行区分,然后将所有model的数据存储到redux中的store里面。在引用的时候,通过各个model的namespace进行引用。
Model是一个处理数据的地方,在model里面调用service层获取数据。
routes:将component组件和store里面的数据进行包装,生成一个新的有数据的组件,然后在router.js
配置文件中引用routes中的组件。
services:负责向后台请求数据,在services里调用后台提供的api获取数据。
utils:工具类,比如常见的后台接口请求工具类。
router.js:配置整个应用的路由
index.js:中仍然应用的入口文件。
通过 npm 安装 antd 和 babel-plugin-import 。
babel-plugin-import 是用来按需加载 antd 的脚本和样式的。
$ npm install antd babel-plugin-import --save
编辑 .roadhogrc
,使 babel-plugin-import 插件生效。
{
"extraBabelPlugins": [
["import", {"libraryName": "antd", "libraryDirectory": "es", "style": "css"}]
}
用 dva-cli 生成路由:
$ dva g route list
routes里自动生成文件
打开router.js
,将新生成的route移动到switch里。
编辑List.js
import React from 'react';
import { connect } from 'dva';
import styles from './List.css';
import { Table, Divider, Tag } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
render: tags => (
<span>
{tags.map(tag => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return (
<Tag color={color} key={tag}>
{tag.toUpperCase()}
</Tag>
);
})}
</span>
),
},
{
title: 'Action',
key: 'action',
render: (text, record) => (
<span>
<a>Invite {record.name}</a>
<Divider type="vertical" />
<a>Delete</a>
</span>
),
},
];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
function List() {
return (
<div className={styles.normal}>
<Table columns={columns} dataSource={data} />
</div>
);
}
function mapStateToProps() {
return {};
}
export default connect(mapStateToProps)(List);
$ dva g model list
编辑list.js
export default {
// 当前model的名称。
// 整个应用的state,由多个小的model的state以namespace为key合成
namespace: 'list',
// 该model当前的状态。
// 数据保存在这里,决定了视图层的输出
state: {
data: [],
},
// action处理器 处理同步动作,更新state
reducers: {
save(state, action) {
return { ...state, ...action.payload }
}
},
// Action处理器,处理异步动作
effects: {
*fetch({ payload }, { call, put }) {
yield put({ type: 'save' })
}
},
// 订阅事件 解决传值问题
// 兄弟组件间通信不必再像使用props那种通过父组件来通信,
// 多层组件之间通信也不必在一层一层的传递,
// 直接在触发事件的组件中发布消息 监听组件中订阅消息即可
subscriptions: {
setup({ dispatch, history }) {
}
},
};
可以看到component List中末尾有这一行
在model/list.js中的effects
中添加:
*getUser({ payload }, { call, put }) {
const res = yield call(service.get);
console.log(res);
},
修改router/List.js
import React from 'react';
import { connect } from 'dva';
import styles from './List.css';
import { Table, Divider, Tag } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
render: tags => (
<span>
{tags.map(tag => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return (
<Tag color={color} key={tag}>
{tag.toUpperCase()}
</Tag>
);
})}
</span>
),
},
{
title: 'Action',
key: 'action',
render: (text, record) => (
<span>
<a>Invite {record.name}</a>
<Divider type="vertical" />
<a>Delete</a>
</span>
),
},
];
// const data = [
// {
// key: '1',
// name: 'John Brown',
// age: 32,
// address: 'New York No. 1 Lake Park',
// tags: ['nice', 'developer'],
// },
// {
// key: '2',
// name: 'Jim Green',
// age: 42,
// address: 'London No. 1 Lake Park',
// tags: ['loser'],
// },
// {
// key: '3',
// name: 'Joe Black',
// age: 32,
// address: 'Sidney No. 1 Lake Park',
// tags: ['cool', 'teacher'],
// },
// ];
function List({dispatch,data}){
return (
<div className={styles.normal}>
<Table columns={columns} dataSource={data} />
</div>
);
}
function mapStateToProps(state) {
return {
data:state.list.data
};
}
export default connect(mapStateToProps)(List);
创建services/list.js
编辑services/list.js,模拟后台数据处理
import request from '../utils/request';
const data = [
{
key: '1',
name: '模拟数据1',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: '模拟数据2',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: '模拟数据3',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
export function test() {
return request('/users', {
method: 'get'
})
}
export function get() {
saveUsers(data); // 存储在本地查看
return data; // 模拟后台返回数据
}
export function add(values) {
// 获取最新人员信息
const users = queryUsers();
values.key = Math.random();
// 模拟后台处理新增的人员信息,直接添加到人员列表
users.push(values);
// 保存处理后最新的人员信息
saveUsers(data); // 存储在本地查看
return data; // 模拟后台返回数据
}
export function deleteUser(values) {
// 获取最新的人员信息
const users = queryUsers();
// 模拟后台处理删除的人员信息,使用filter过滤掉已经删除的人员
const newUsers = users.filter(item => item.key !== values.key);
// 保存处理后最新的人员信息
saveUsers(newUsers); // 存储在本地,方便查看
return newUsers; // 模拟后台返回的数据
}
export function editUser(values) {
// 获取最新的人员信息
const users = queryUsers();
// 模拟后台处理新增的人员信息,直接添加到人员列表里面
const newUsers = users.map((item) => {
if (item.key === values.key) { // 相等,代表这个人员信息已被修改,需要用新的人员消息替换旧的人员信息;
return values;
} else { // 不相等,代表这个人员信息未被修改,直接返回该人员信息;
return item;
}
});
// 保存处理后最新的人员信息
saveUsers(newUsers); // 存储在本地,方便查看
return newUsers; // 模拟后台返回的数据
}
const saveUsers = (users) => {
localStorage.setItem("users", JSON.stringify(users));
};
const queryUsers = () => {
return JSON.parse(localStorage.getItem("users"));
};
在models/list.js的effects
中添加:
*getUser({ payload }, { call, put }) {
const data = yield call(listService.get);
yield put({ type: 'save', payload: { data } });
listService:import * as listService from ‘…/services/list’
在routes/List.js中添加:
useEffect(() => {
dispatch({ type: 'list/getUser' })
}, []);
修改route/List.js页面,增加新增按钮和点击事件
import React, { useEffect, useState } from 'react';
import { connect } from 'dva';
import styles from './List.css';
import { Table, Divider, Tag, Button, Modal, Form, Input, Row, Col } from 'antd';
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: text => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
render: tags => (
<span>
{tags.map(tag => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return (
<Tag color={color} key={tag}>
{tag.toUpperCase()}
</Tag>
);
})}
</span>
),
},
{
title: 'Action',
key: 'action',
render: (text, record) => (
<span>
<a>Invite {record.name}</a>
<Divider type="vertical" />
<a>Delete</a>
</span>
),
},
];
function List({ dispatch, data }) {
useEffect(() => {
dispatch({ type: 'list/getUser' })
}, []);
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const handleOk = (values) => {
console.log(values);
setIsModalOpen(false);
};
const handleCancel = () => {
setIsModalOpen(false);
};
return (
<div className={styles.normal}>
<Button type="primary" onClick={showModal}>
新增
</Button>
<Modal title="Basic Modal" open={isModalOpen} footer={null} onOk={handleOk} onCancel={handleCancel}>
<Form
name="basic"
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
autoComplete="off"
onFinish={handleOk}
>
<Form.Item
label="name"
name="name"
rules={[{ required: true, message: 'Please input your name!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="age"
name="age"
rules={[{ required: true, message: 'Please input your age!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="address"
name="address"
rules={[{ required: true, message: 'Please input your address!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="tags"
name="tags"
rules={[{ required: true, message: 'Please input your tags!' }]}
>
<Input />
</Form.Item>
<Form.Item >
<Row>
<Col span={12} >
<Button type="primary" htmlType="submit">
确认
</Button>
</Col>
<Col span={12}>
<Button htmlType="button" onClick={handleCancel}>
取消
</Button>
</Col>
</Row>
</Form.Item>
</Form>
</Modal>
<Table columns={columns} dataSource={data} />
</div>
);
}
function mapStateToProps(state) {
return {
data: state.list.data
};
}
export default connect(mapStateToProps)(List);
修改提交方法
const handleOk = (values) => {
console.log(values);
dispatch({
type:'list/addUser',
payload:values
})
setIsModalOpen(false);
};
添加model中的effcet
*addUser({payload},{call,put}){
const data = yield call(listService.add,payload);
yield put({ type: 'save', payload: { data } });
}