Dva搭建项目
2018/11/16 18:19 分类: 技术交流 浏览:120
DvaJS指南(推荐先去看一下)
dva 是一个基于 React 和 Redux 的轻量应用框架,概念来自 elm,支持 side effects、热替换、动态加载、react-native、SSR 等,已在生产环境广泛应用。
Github地址(里面有很多教程)
https://github.com/dvajs/dva/blob/master/README_zh-CN.md
AntD快速搭建教程:
一、安装 dva-cli
dva-cli 是 dva 的命令行工具,包含 init、new、generate 等功能,目前最重要的功能是可以快速生成项目以及你所需要的代码片段。
通过 npm 安装 dva-cli 并确保版本是 0.9.1 或以上。
$ npm install dva-cli -g
安装完成后,可以通过 dva -v 查看版本,以及 dva -h 查看帮助信息。
二、创建新应用
安装完 dva-cli 之后,可以通过 dva new projectName 创建新应用(projectName表示项目名称,根据需求自定义)。
$ dva new dva-quickstart
这会创建 dva-quickstart 目录,包含项目初始化目录和文件,并提供开发服务器、构建脚本、数据 mock 服务、代理服务器等功能。
然后我们 cd 进入 dva-quickstart 目录,并启动开发服务器:
$ cd dva-quickstart
$ npm start
启动成功后,在浏览器里打开 http://localhost:8000 ,你会看到 dva 的欢迎界面。
项目结构说明
①components:最基础的组件。这里面存放的只是最基本的UI组件,这些组件接收外部传过来的参数(数据),并将这些数据渲染的到界面。根据传入的参数的不同,界面渲染也不同。
三、使用 antd
②container(对应dva开发的项目目录routers):contatiner负责将数据的组件进行连接,相当于将compontent组件和store里面的数据进行包装,生成一个新的有数据的组件。然后,在router.js配置文件中引用container中的组件。 ③routers:router目录其实和container目录基本一样,只是在利用dva开发的应用中叫router,在不是利用dva开发的应用中叫container而已,两者有一个即可。 ④models:model是dva中的一个重要概念,也可以看作是前端中的数据层。State是整个应用的数据层。应用的state被存储在一个object tree中。应用的初始state在model中定义,也就是说,由model state组成全局state。dva将model以namespace作为唯一标识进行区分,然后将所有model的数据存储到redux中的store里面。在引用的时候,通过各个model的namespace进行引用。Model,是一个处理数据的地方,在model里面调用service层获取数据。 ⑤services:services负责向后台请求数据,在services里调用后台提供的api获取数据。 ⑥utils:工具类目录,比如常见的后台接口请求工具类。 ⑦styles:存放css或less样式文件。 ⑧constants.js:在里面定义一些通用的常量。 ⑨router.js:配置整个应用的路由。 ⑩index.js:整个应用的入口文件,dva和其它框架稍有不同。 通过 npm 安装 antd 和 babel-plugin-import 。babel-plugin-import 是用来按需加载 antd 的脚本和样式的,详见 repo 。
$ npm install antd babel-plugin-import --save
编辑 .webpackrc,使 babel-plugin-import 插件生效。
{
"extraBabelPlugins": [
["import", {"libraryName": "antd", "libraryDirectory": "es", "style": "css"}]
}
四、添加列表页面 在src/routes目录中新建demo文件夹,添加List.js页面。如下:
此处使用的是antd的Table组件
import React, { Component } from 'react';
import { Table, Divider, Tag } from 'antd'; 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'], }]; class DemoPage extends Component { columns = [{
title: 'Name', dataIndex: 'name', key: 'name', render: text => <a href="javascript:;">{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 => <Tag color="blue" key={tag}>{tag}</Tag>)} </span> ), }, { title: 'Action', key: 'action', render: (text, record) => ( <span> <a>编辑</a> <Divider type="vertical" /> <a>删除</a> </span> ), }]; render(){ return (<div> <h1>列表页面</h1> <Button type={'primary'} >新增</Button>
<Table columns={this.columns} dataSource={data} /> </div>); } } export default DemoPage;
五、添加路由 添加路由后,可以通过相应的路径访问到添加到页面。
在src/router.js文件中添加
import Demo from './routes/demo/List';
<Route path="/demo" exact component={Demo} />
浏览器地址栏输入http://localhost:8000/#/demo , 可以看见添加的list页面
六、定义model 1、在src/models目录中添加demo.js
import * as service from '../services/demo'; export default { namespace: 'demo', // 当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace为 key 合成 state: { //该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出 data: [], }, // 订阅事件 subscriptions: { setup({ dispatch, history }) { // eslint-disable-line }, }, // Action 处理器,处理异步动作 effects: {
*fetch({ payload }, { call, put }) { // eslint-disable-line yield put({ type: 'save' }); } }, // Action 处理器,处理同步动作,用来算出最新的 State reducers: { save(state, action) { return { ...state, ...action.payload }; }, }, }; 2、在src/index.js中加载这个model,添加如下代码
app.model(require('./models/demo').default);
七、使用connect绑定models 绑定后可以在this.props里面获取到对应的model的state数据
1、在List.js文件中引入 connect
import { connect } from 'dva';
2、修改最后一行
export default DemoPage 为:
export default connect((state)=> {
return { demo: state.demo }; })(DemoPage);
3、在models/demo.js文件的effects里添加方法:
*getUsers ({ payload }, { call, put }) {
const res = yield call(service.get); console.log(res); }, 3、修改render里
const data = this.props.demo.data;
刷新页面可以看见页面数据已经变成models里面的数据。
八、获取数据 页面渲染完成后,发送请求去获取实时的数据
1、 在src/services添加demo.js,模拟后台数据处理
import request from '../utils/request';
const data = [{ key: '1', name: '模拟请求获取的数据 张三', age: 32, address: 'New York No. 1 Lake Park', tags: ['nice', 'developer'], }, { key: '2', name: '模拟请求获取的数据 李四', age: 42, address: 'London No. 1 Lake Park', tags: ['loser'], }, { key: '3', name: '模拟请求获取的数据 王二', 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(users); // // 存储在本地,方便查看 return users; // 模拟后台返回的数据 } 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")); }; 2、在models/demo.js的effects里添加方法
*getUsers ({ payload }, { call, put }) {
const data = yield call(service.get); yield put({ type: 'save', payload: { data } }); }, 3、在List.js页面添加方法
componentDidMount = () => {
this.props.dispatch({ type: 'demo/getUsers', }) }; 刷新页面可以看机页面数据已经更新;
九、添加新数据
List.js页面
1、在新增按钮上添加点击事件
<Button type={'primary'} onClick={this.showModal} >新增</Button>
2、引入modal模态框组件,用来添加新的人员,
替换后页面代码:
import React, { Component } from 'react';
import { Table, Divider, Tag, Button, Modal } from 'antd'; import { connect } from 'dva'; class DemoPage extends Component { state = { visible: false }; showModal = () => { this.setState({ visible: true, }); } handleOk = (e) => { console.log(e); this.setState({ visible: false, }); } handleCancel = (e) => { console.log(e); this.setState({ visible: false, }); } componentDidMount = () => { this.props.dispatch({ type: 'demo/getUsers', payload: true, }) }; 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 => <Tag color="blue" key={tag}>{tag}</Tag>)} </span> ), }, { title: 'Action', key: 'action', render: (text, record) => ( <span> <a>编辑</a> <Divider type="vertical" /> <a onClick={this.handleDelete} >删除</a> </span> ), } ]; render(){ const data = this.props.demo.data; return (<div> <h1>列表页面</h1> <Button type={'primary'} onClick={this.showModal} >新增</Button> <Table columns={columns} dataSource={data} /> <Modal title="Basic Modal" visible={this.state.visible} onOk={this.handleOk} onCancel={this.handleCancel} > <p>Some contents...</p> <p>Some contents...</p> <p>Some contents...</p> </Modal> </div>); } } DemoPage.propTypes = { }; export default connect((state)=> { return { demo: state.demo }; })(DemoPage); 3、替换模态框里面的内容为表单,
注意:必须使用Form.create()调用组件,使用方式如下:
class DemoPage extends React.Component {}
DemoPage = Form.create({})(DemoPage);
替换后页面代码:
import React, { Component } from 'react';
import { Table, Divider, Tag, Button, Modal, Form, Input, Icon } from 'antd'; import { connect } from 'dva'; const FormItem = Form.Item; class DemoPage extends Component {
state = { visible: false }; showModal = () => { this.setState({ visible: true, }); } handleOk = (e) => { this.setState({ visible: false, }); } handleCancel = (e) => { console.log(e); this.setState({ visible: false, }); } componentDidMount = () => { this.props.dispatch({ type: 'demo/getUsers', payload: true, }) }; 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 => <Tag color="blue" key={tag}>{tag}</Tag>)} </span> ), }, { title: 'Action', key: 'action', render: (text, record) => ( <span> <a>编辑</a> <Divider type="vertical" /> <a onClick={this.handleDelete} >删除</a> </span> ), } ]; render(){ const data = this.props.demo.data; const { getFieldDecorator } = this.props.form; return (<div> <h1>列表页面</h1> <Button type={'primary'} onClick={this.showModal} >新增</Button> <Table columns={columns} dataSource={data} /> <Modal title="Basic Modal" visible={this.state.visible} onOk={this.handleOk} onCancel={this.handleCancel} > <Form className="login-form"> <FormItem> {getFieldDecorator('name', { rules: [{ required: true, message: 'Please input your 名称!' }], })( <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="名称" /> )} </FormItem> <FormItem> {getFieldDecorator('age', { rules: [{ required: true, message: 'Please input your 年龄!' }], })( <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="年龄" /> )} </FormItem> <FormItem> {getFieldDecorator('address', { rules: [{ required: true, message: 'Please input your 地址!' }], })( <Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="地址" /> )} </FormItem> <FormItem>
{getFieldDecorator('tags', { rules: [{ required: true, message: 'Please input your 标签!' }], })( <Select mode="tags" style={{ width: '100%' }} placeholder="标签" /> )} </FormItem> </Form> </Modal> </div>); } } DemoPage.propTypes = { }; DemoPage = Form.create()(DemoPage); export default connect((state)=> { return { demo: state.demo }; })(DemoPage); 4、修改提交方法,点击模态框的确定按钮时,提交新增人员信息
handleOk = (e) => {
e.preventDefault(); this.props.form.validateFields((err, values) => { if (!err) { console.log('Received values of form: ', values); this.props.dispatch({ type: 'demo/addUser', payload: values, }) } }); this.setState({ visible: false, }); } 5、在models/demo.js 的effects里面添加方法
*addUser ({ payload }, { call, put }) {
const data = yield call(service.add, payload); yield put({ type: 'save', payload: { data } }); }, 十、删除数据 1、删除按钮添加点击事件
<a onClick={() => this.handleDelete(record)} >删除</a>
2、新增删除方法
handleDelete =(record) => {
this.props.dispatch({ type: 'demo/deleteUser', payload: record, }); }; 3、models/demo.js 的effects里添加方法
*deleteUser ({ payload }, { call, put }) {
十一、编辑数据
const data = yield call(service.deleteUser, payload); yield put({ type: 'save', payload: { data } }); }, 这里复用添加新数据的模态框,修改后代码:
import React, { Component } from 'react';
import { Table, Divider, Tag, Button, Modal, Form, Input, Icon, Select } from 'antd'; import { connect } from 'dva'; const FormItem = Form.Item; class DemoPage extends Component { state = { visible: false, selectedValue: {}}; // 打开模态框,保存传入的selectedValue值,用于编辑时回显 showModal = (selectedValue = {}) => { this.setState({ visible: true, selectedValue, }); }; handleOk = (e) => { e.preventDefault(); this.props.form.validateFields((err, values) => { if (!err) { console.log('Received values of form: ', values); let type = 'demo/addUser'; const selectedValue = this.state.selectedValue; if(selectedValue.key) { // 编辑,处理数据,key作为人员的唯一标识符,编辑时不变 type = 'demo/editUser'; values.key = selectedValue.key; } this.props.dispatch({ type, payload: values, }) // 关闭模态框 this.setState({ visible: false, }); // 重置表单的值,避免打开时出现上次编辑的数据 this.props.form.resetFields(); } }); }; handleCancel = (e) => { console.log(e); this.setState({ visible: false, }); // 重置表单的值,避免打开时出现上次编辑的数据 this.props.form.resetFields(); }; // 页面加载完成后获取用户信息 componentDidMount = () => { this.props.dispatch({ type: 'demo/getUsers', }) }; // 处理删除 handleDelete =(record) => { this.props.dispatch({ type: 'demo/deleteUser', payload: record, }); }; // table的列信息 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 => <Tag color="blue" key={tag}>{tag}</Tag>)} </span> ), }, { title: 'Action', key: 'action', render: (text, record) => ( <span> <a onClick={() => this.showModal(record)} >编辑</a> <Divider type="vertical" /> <a onClick={() => this.handleDelete(record)} >删除</a> </span> ), } ]; render(){ const data = this.props.demo.data; // 给表单注册属性名时使用 const { getFieldDecorator } = this.props.form; return (<div> <h1>列表页面</h1> <Button type={'primary'} onClick={this.showModal} >新增</Button> <Table columns={this.columns} dataSource={data} /> <Modal title="Basic Modal" visible={this.state.visible} onOk={this.handleOk} onCancel={this.handleCancel} > <Form className="login-form"> <FormItem> {/* 把name属性注册到表单里*/} {getFieldDecorator('name', { initialValue: this.state.selectedValue.name, // 默认值 rules: [{ required: true, message: 'Please input your 名称!' }], // 验证规则 })( <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="名称" /> )} </FormItem> <FormItem> {getFieldDecorator('age', { initialValue: this.state.selectedValue.age, rules: [{ required: true, message: 'Please input your 年龄!' }], })( <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="年龄" /> )} </FormItem> <FormItem> {getFieldDecorator('address', { initialValue: this.state.selectedValue.address, rules: [{ required: true, message: 'Please input your 地址!' }], })( <Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="地址" /> )} </FormItem> <FormItem> {getFieldDecorator('tags', { initialValue: this.state.selectedValue.tags || ['cool'], rules: [{ required: true, message: 'Please input your 标签!' }], })( <Select mode="tags" style={{ width: '100%' }} placeholder="标签" /> )} </FormItem> </Form> </Modal> </div>); } } DemoPage.propTypes = { }; DemoPage = Form.create()(DemoPage); export default connect((state)=> { return { demo: state.demo }; })(DemoPage); 如果你已经进行到这里,DVA入门这一步你已经成功迈出了。
感谢源码时代教学讲师提供此文章!
本文为原创文章,转载请注明出处!
|
赞 0