React + Dva + Antd项目搭建入门

React + Dva + Antd项目搭建入门_第1张图片

本项目搭建过程参考 Dva官方快速上手教程
项目环境:(在该环境下测试通过,默认使用cnpm)
dva 0.9.2
node 8.10.0

安装 dva-cli

确保版本在 0.9.1 及以上

$ npm install dva-cli -g
$ dva -v
dva-cli version 0.9.2

创建新应用

$ dva new dva-demo

完成之后进入生成的项目文件夹并启动项目

$ cd dva-demo
$ npm start

在浏览器里打开 http://localhost:8000
React + Dva + Antd项目搭建入门_第2张图片

使用 antd

antd 是淘宝前端团队开源的一个UI库

安装 antd 和 babel-plugin-import(babel-plugin-import 用来按需加载 antd 的脚本和样式)

$ cnpm install antd babel-plugin-import --save

编辑 .webpackrc,使 babel-plugin-import 插件生效(代码每行首个字符为 + 表示该行为新增内容,下同)

{
+  "extraBabelPlugins": [
+    ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
+  ]
}

定义路由

创建一个数据列表页面,实现增删改查

新建路由组件routes/Products.js

import React from 'react';

const Products = (props) => (
  

List of Products

); export default Products;

添加路由信息到路由表 router.js

+ import Products from './routes/Products';
...
+ 

然后在浏览器里打开 http://localhost:8000/#/products

编写 UI 组件

新建 components/ProductList.js 文件

import React from 'react';
import PropTypes from 'prop-types';
import { Table, Popconfirm, Button } from 'antd';

const ProductList = ({ products }) => {
  const columns = [{
    title: '名称',
    dataIndex: 'name',
  }, {
    title: '描述',
    dataIndex: 'desc'
  }];
  return (
    
  );
};

ProductList.propTypes = {
  products: PropTypes.array.isRequired,
};

export default ProductList;

定义 Model

现在开始处理数据和逻辑:dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions 。

  • namespace 表示在全局 state 上的 key
  • state 是初始值,在这里是空数组
  • reducers 等同于 redux 里的 reducer,接收action,同步更新 state
    新建 model models/products.js
export default {
  namespace: 'products',
  state: [],
  reducers: {},
};

index.js 里载入model

// 3. Model
+ app.model(require('./models/products').default);

connect 起来

dva 提供了 connect 方法将 model 和 component 串联起来
编辑 routes/Products.js

import React from 'react';
import { connect } from 'dva';
import ProductList from '../components/ProductList';

const Products = ({ dispatch, products }) => {
  return (
    

List of Products

); }; // export default Products; export default connect(({ products }) => ({ products, }))(Products);

在 model 里面添加初始数据 models/products.js

export default {
  namespace: 'products',
  state: [
    {
      id: 1,
      name: 'dva',
      desc: 'dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。'
    },
    {
      id: 2,
      name: 'antd',
      desc: '服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。'
    },
  ],
  reducers: {},
};

刷新浏览器看到以下效果
React + Dva + Antd项目搭建入门_第3张图片

添加删除等功能

编辑 components/ProductList.js

+ import { Table, Popconfirm, Button } from 'antd';

+ const ProductList = ({ onDelete, products }) => {
...
+   }, {
+     title: 'Actions',
+     render: (text, record) => {
+       return (
+          onDelete(record.id)}>
+            
+         
+       );
+     },
+   }];
... 
ProductList.propTypes = {
+   onDelete: PropTypes.func.isRequired,
  products: PropTypes.array.isRequired,
};
...

编辑 models/products.js

reducers: {
+    'delete'(state, { payload: id }) {
+       return state.filter(item => item.id !== id);
+     },
  },

编辑 routes/Products.js

+ const Products = ({ dispatch, products }) => {
+   function handleDelete(id) {
+     dispatch({
+       type: 'products/delete',
+       payload: id,
+     });
+   }
  return (
    

List of Products

+
); };

刷新浏览器看到以下效果
React + Dva + Antd项目搭建入门_第4张图片
React + Dva + Antd项目搭建入门_第5张图片

使用 Class 语法创建拥有 增/删/改/查 功能的数据列表页面

定义路由

新建路由组件routes/Users.js

import React, { Component } from 'react';
import { connect } from 'dva';
import styles from './Users.less';
import { LocaleProvider } from 'antd';
import zhCN from 'antd/lib/locale-provider/zh_CN';

import { Table, Popconfirm, Button } from 'antd';
import * as lodash from 'lodash';
import EditModal from '../components/EditModal';


class Users extends Component {
  constructor(props) {
    super(props);
    this.state = {
      editModalVisible: false, // 修改弹框显示状态
      selectedRecord: null, // 当前选中的记录
    };
    this.columns = [{
      title: 'ID',
      dataIndex: 'id',
      }, {
        title: '名称',
        dataIndex: 'name',
      }, {
        title: '性别',
        dataIndex: 'sex',
        render: (text) => {
          return (
            text === 'F' ? '女' : '男'
          );
        },
      }, {
        title: '年龄',
        dataIndex: 'age'
      }, {
        title: '操作',
        render: (text, record) => {
          return (
            
this.onDelete(record.id)}>
); }, } ]; } componentWillMount() { this.props.dispatch({ type: 'users/init'}); } componentDidMount() { } componentWillReceiveProps(nextProps) { } handleAdd = () => { this.setState({ editModalVisible: true, }) } onDelete = (id) => { let { dispatch, userList } = this.props; let newUserList = userList.filter(user => user.id != id); dispatch({ type: 'users/saveUserList', payload: newUserList }) } onShowEidtModal = (record) => { console.log('onShowEditModal(), record:', record); this.setState({ editModalVisible: true, selectedRecord: record, }); } handleOk = (newUser, type) => { console.log('handleOk(), newUser:', newUser); const { dispatch, userList } = this.props; let newUserList = userList || []; if (type == 0) { newUserList.push(newUser); } else if (type == 1 && !!newUserList) { for(let i = 0; i < newUserList.length; i ++) { if (newUserList[i].id == newUser.id) { newUserList[i] = newUser; } } } dispatch({ type: 'users/saveUserList', payload: newUserList, }) this.setState({ editModalVisible: false, selectedRecord: null, }) } handleCancel = (e) => { console.log('handleCancel:', e); this.setState({ editModalVisible: false, selectedRecord: null, }) } render() { const { userList } = this.props; console.log('users => render(), userList:', userList); const { editModalVisible, selectedRecord } = this.state; console.log('render(), editModalVisible:', editModalVisible, 'selectedRecord:', selectedRecord); return (
{ editModalVisible && } ); } } function mapStateToProps(state) { const { userList } = state.users; return { userList, }; } export default connect(mapStateToProps)(Users);

新建 users/Users.less

.container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  background-color: antiquewhite;
}

添加路由信息到路由表 router.js

+ import Users from './routes/Users';
...
+ 

然后在浏览器里打开 http://localhost:8000/#/users

编写 UI 组件

新建 components/EditModal.js 文件

import React, { Component } from 'react';
import styles from './EditModal.less';
import {
  Modal,
  Form,
  Input,
  DatePicker,
  TimePicker,
  Select,
  Cascader,
  InputNumber,
}
from 'antd';
const uuidv1 = require('uuid/v1');

const { Option } = Select;

class EditModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      id: !!props.data && props.data.id || null,
      name: !!props.data && props.data.name || null, // 当前选中的记录
      sex: !!props.data && props.data.sex || null, // 当前选中的记录
      age: !!props.data && props.data.age || null, // 当前选中的记录
    };
  }

  handleOk = (e) => {
    console.log(e);
    const { id, name, age, sex } = this.state;
    let user = {
      id,
      name,
      age,
      sex,
    };
    if (!id || id === 'undefined' || id === '') {
      user.id = uuidv1();
      this.props.handleOk(user, 0);
    } else {
      this.props.handleOk(user, 1);
    }
  }

  handleCancel = (e) => {
    console.log(e);
    this.props.handleCancel(e);
  }

  handleChangeName = (e) => {
    this.setState({
      name: e.target.value,
    })
  }

  handleChangeSex = (sex) => {
    console.log('handleChangeSex(), sex:', sex);
    this.setState({
      sex,
    })
  }

  handleChangeAge = (e) => {
    this.setState({
      age: e.target.value,
    })
  }

  getFormDom() {
    const { name, age, sex } = this.state;
    return (
      
姓名
性别
年龄
) } render() { return (
{this.getFormDom()}
); } } export default EditModal;

新建 components/EditModal.less 文件

.form {
  > div {
    display: flex;
    margin: 10px 0;
    text-align: center;
    align-items: center;
    justify-content: flex-start;

    > lable {
      width: 80px;
    }
  }
}

定义 Model

新建 model models/users.js

export default {
  namespace: 'users',
  state: {
    userList: [],
  },
  reducers: {
    saveUserList(state, { payload }) {
      console.log('model users => reducers/saveUserList(), payload:', payload);
      return { ...state, userList: Object.assign([], payload || []) };
    }
  },
  effects: {
    *init(_, { call, put, select }) {
      //获取后台数据
      const userList = [{
        id: 1,
        name: '张三',
        age: 13,
        sex: 'F'
      }, {
        id: 2,
        name: '张四',
        age: 14,
        sex: 'M'
      }, {
        id: 3,
        name: '张五',
        age: 15,
        sex: 'F'
      }, {
        id: 4,
        name: '张六',
        age: 16,
        sex: 'M'
      }, {
        id: 5,
        name: '张七',
        age: 16,
        sex: 'F'
      }];
      yield put({
        type: 'saveUserList',
        payload: userList
      });
    }
  },
  subscriptions: {
    setup({ dispatch, history }) {
      return history.listen(({ pathname, query }) => {
      });
    }
  },
};

index.js 里载入model

// 3. Model
+ app.model(require('./models/users').default);

然后在浏览器里打开 http://localhost:8000/#/users
看到如下效果
React + Dva + Antd项目搭建入门_第6张图片
React + Dva + Antd项目搭建入门_第7张图片
React + Dva + Antd项目搭建入门_第8张图片
到此完成基本功能,详见源码 github
文中若有错误或者需要改进的地方,欢迎大家留言指正,或到github提交issues

你可能感兴趣的:(React,JavaScript)