最近公司做了一个系统,因为页面涉及的表单交互非常多,如果使用之前的 Node + Express 的开发模式效率是非常低的,因此经过考虑,最后决定使用 Node + React 的开发模式,并且使用了蚂蚁金服出品的开源框架 Ant Design。

正如Ant Design 官方介绍: "在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,给设计师和工程师带来很多困扰和重复建设,大大降低了产品的研发效率。"在这次开发的项目中,因为数据交互非常频繁,大量的表单、时间选择器、表格、单选多选框,以及各种标识状态的组件,如果光靠手写或者使用 jQuery 插件,其开发的工作量仍然是相当大的,因此,Ant Design "经过大量的项目实践和总结,沉淀出一个中台设计语言 Ant Design。旨在统一中台项目的前端 UI 设计,屏蔽不必要的设计差异和实现成本,解放设计和前端的研发资源。"

同时,React 也是第一次使用,起初对于这种 Html 与 JS 代码混杂的书写格式搞得很不舒服,因为作为一个前端开发者,被"结构与逻辑分离"的思想洗脑太久了,但实际上组件的 HTML 是组成一个组件不可分割的一部分,能够将 HTML 封装起来才是组件的完全体,React 发明了 JSX 让 JS 支持嵌入 HTML 不得不说是一种非常聪明的做法,让前端实现真正意义上的组件化成为了可能。所以在使用了 React 开发一段时间后,越来越感觉到 React 封装组件的高效。而 Ant Design 则封装了一系列高质量的 React 组件,十分适用于在企业级的应用中,框架提供的 api 十分详尽,上手和使用相对简单,值得一提的是, Ant Design 使用 ES6 进行编写,因此使用过程中对 ES6 也是一次学习的机会。

在这里主要就项目中使用到的主要的组件以及遇到的一些坑进行总结,也是对这个项目开发的一个简单的梳理。

项目开发中主要用到的组件:

Button、Icon、Row/Col、BackTop、Pagination、Tabs、Checkbox、Cascader、Form、Rate、Select 、Modal、Message、Table

Button 的使用比较简单,需要注意的地方是为了和原生 button 进行区分,Form表单中的 Button 组件的submit 需要写成 htmlType = "submit" ,其他类似的组件也是同理。

举例:

使用了两种类型的Button, 并且定义了 Button 的点击事件,事件在 render() 方法外进行定义。

Icon的使用非常简单,Ant Design 提供了常用了Icon , 使用时只需要点击图标即可完成代码的复制。Icon 自带两个属性,type 定义了图标的类型, spin 定义图标是否有旋转动画。

Row / Col 主要用于栅格布局,和BootStrap 的使用十分相似,不同的是 Ant Design 默认把页面分成24份,而不是12份,增加了布局的灵活度。

Tabs 十分实用,通过Tabs 的切换可以十分高效地在同一个页面中展现不同业务的信息。Tabs 的使用也十分简单,只要将每一个 Tab 面板放在 Tabpane 标签里面即可。值得一提的是,如果要实现标签的动态切换(根据 state 的状态进行切换),需要在 Tabs 标签中添加 activeKey 属性,别且绑定 到具体的控制标签切换的 state 上,更重要的是,需要添加 onChange 方法,在标签切换的事件中设置当前 tab 的 state,如下:

Form 是项目中用的最多的表单了,表单项通过 FormItem 进行定义,并且可以设置对其方式、验证规则、错误提示信息以及绑定初始值,十分有用。

在最外层的 Form 标签上定义了表单的提交方法,并且把所有的表单项都获取复制到 values 参数中,因此可以对 values 对象进行操作,获取到对象的字段值并经行处理,如下:

Modal 主要在弹框和提示框中使用,如果弹框中需要进行数据操作,如选择和填写信息,则直接使用 Modal ,定义标题,按钮方法,以及弹框中的具体内容;如果只是简单的信息提示,比如成功或失败、警告等,则可以根据场景使用 Modal 下的 success、warning、confirm、info方法,使用起来也十分方便,需要注意的是,在这些方法中无法获取到正确的 this 指向,因此需要在方法外部先获取到 this 指针。如下:

Table 的的使用过中则遇到了很多的坑,主要是因为表格中的数据都是通过请求接口获取到的,因此需要对数据进行动态渲染,同时表格中的数据还允许操作,甚至,有多个表格需要同时渲染,因此表格的一切都要再请求接口后动态生成,然后渲染到 DOM 结构中去。举例:

在这里表格中的数据通过请求接口获取,每一个 item 包含了表格中的所有数据,同时手动为表格中的每一行数据添加一个 index 属性用于标识数据的唯一性(所有通过 for 或者 map 循环出来的数据都需要加标识条目唯一性的 key 值,可以是 id,也可以是自己添加的属性,唯一即可,不这样做的话浏览器会报出警告)。

当获取到数据并生成所有的表格结构后,将此对象复制给 state,并插入到 DOM,便可以实现动态表格以及动态数据的插入。表格的使用,谨慎仔细是十分重要的。

Cascader 在封装选择省市区组件中使用到,这里也有较多的技巧。组件代码如下:

 import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import Cascader from 'antd/lib/cascader'
import Button from 'antd/lib/button'
import message from 'antd/lib/message'
import ajax from '../../utils/service'
import * as inputActions from '../../actions/input' let province = [
{ value: 2, label: "北京", isLeaf: false },
{ value: 3, label: "安徽", isLeaf: false },
{ value: 4, label: "福建", isLeaf: false },
{ value: 5, label: "甘肃", isLeaf: false },
{ value: 6, label: "广东", isLeaf: false },
{ value: 7, label: "广西", isLeaf: false },
{ value: 8, label: "贵州", isLeaf: false },
{ value: 9, label: "海南", isLeaf: false },
{ value: 10, label: "河北", isLeaf: false },
{ value: 11, label: "河南", isLeaf: false },
{ value: 12, label: "黑龙江", isLeaf: false },
{ value: 13, label: "湖北", isLeaf: false },
{ value: 14, label: "湖南", isLeaf: false },
{ value: 15, label: "吉林", isLeaf: false },
{ value: 16, label: "江苏", isLeaf: false },
{ value: 17, label: "江西", isLeaf: false },
{ value: 18, label: "辽宁", isLeaf: false },
{ value: 19, label: "内蒙古", isLeaf: false },
{ value: 20, label: "宁夏", isLeaf: false },
{ value: 21, label: "青海", isLeaf: false },
{ value: 22, label: "山东", isLeaf: false },
{ value: 23, label: "山西", isLeaf: false },
{ value: 24, label: "陕西", isLeaf: false },
{ value: 25, label: "上海", isLeaf: false },
{ value: 26, label: "四川", isLeaf: false },
{ value: 27, label: "天津", isLeaf: false },
{ value: 28, label: "西藏", isLeaf: false },
{ value: 29, label: "新疆", isLeaf: false },
{ value: 30, label: "云南", isLeaf: false },
{ value: 31, label: "浙江", isLeaf: false },
{ value: 32, label: "重庆", isLeaf: false },
{ value: 33, label: "香港", isLeaf: false },
{ value: 34, label: "澳门", isLeaf: false },
{ value: 35, label: "台湾", isLeaf: false }
]; let options = province class Location extends Component {
constructor(props) {
super(props); }
state = {
options: options,
inputValue: '',
} componentWillMount(){
const {hideLoading} = this.props;
setTimeout(() => { hideLoading()}, 1000)
} _onChange = (value, selectedOptions) => {
console.log(value);
this.setState({
inputValue: selectedOptions.map(o=>o.label).join(', ')
});
const onChange = this.props.onChange; if (onChange) {
onChange({...value});
}
} _loadData = (selectedOptions) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
const id = targetOption.value;
targetOption.loading = true; if (selectedOptions.length == '1') {
// 点击省,获取市
ajax.post(ajax.api.GETSERVICEAREA, { id: id, token: localStorage.token }).then(data => {
if(data.status.code == '1'){
targetOption.loading = false;
targetOption.children = [];
data.result.map((v, k)=>{
// 拼出市
targetOption.children.push({
value: v.id,
label: v.name,
isLeaf: false,
});
});
this.setState({
options: [...this.state.options],
});
}
});
}else if (selectedOptions.length == '2') {
// 点击市,获取区
ajax.post(ajax.api.GETSERVICEAREA, { id: id, token: localStorage.token }).then(data => {
if(data.status.code == '1'){
targetOption.loading = false;
targetOption.children = [];
data.result.map((v, k)=>{
// 拼出区
targetOption.children.push({
value: v.id,
label: v.name,
isLeaf: true,
});
});
this.setState({
options: [...this.state.options],
});
}
});
}else {
targetOption.loading = false;
}
} _resetLocation = () => {
document.getElementsByClassName('ant-cascader-picker-clear')[0].click();
this.setState({
inputValue: '',
});
} render() {
return (
<div>
<Cascader
options={this.state.options}
loadData={this._loadData}
onChange={this._onChange}
changeOnSelect
placeholder=""
/>
</div>
)
}
} export default connect(state => ({
showLoginLayer: state.login.isShowLoginLayer
}), inputActions)(Location)

其中要注意的地方很多,首先是初始值的设置。因为使用省市区组件,不能一次性拉取到所有的地区,这样会十分消耗性能,造成不良的用户体验,正确的做法是在点击时获取下一级的地区信息,这样有针对性的请求可以减少加载时间,那么问题来了,如何实现?

首先,使用 Cascader 的 api 中有 loadData 属性,可以定义数据的加载,同时,onChange 事件可以监听到每一次的数据变化,但是使用 Cascader 有一个限制,便是第一级的数据需要在加载组件之前就定义并获取到,否则无法进行下一级数据的加载,因此这里单独定义了所有了省的数据:

onChange 事件获取到当前选择的对象,并提供了两个参数,分别是选择的当前值(数组类型)和选择的多级对象(当前选择的对象和当前对象下一级的数组对象)。

在获取到当前选中的对象后对值进行拼接处理并赋值给 inputValue (在组件框中显示的选中值),同时设置onChange()方法,将值的变化情况通知给父组件(如 FormItem )

loadData()方法用于组件级联数据的加载,其参数就是 onChange()方法的第二个参数,在这里获取到参数的最后一个对象(即当前点击的对象),通过判断参数的长度来识别当前要获取的数据的类型(市还是区),获取到数据后生成组件要求的数据格式,最后将格式化的数据赋值给 option 就可以了。

最后,模拟了一个重置(清空)的功能,Cascader 值的清空有两点,一是选择框内容的清空,二是级联数据的重置,内容的清空比较简单,直接将 inputValue 设置为空即可,而级联数据的重置不能将 option 设置为空,这样会导致级联组件无法使用(下拉数据为空),为此使用了一个小技巧:

在组件的 api 属性中有一个allowClear 属性,默认是 true,即允许清空,其效果是当鼠标移到选择数据后的级联表单上会出现一个 叉号的按钮,如下:

通过开发者工具可以获取到这个按钮的类名,那么之后的操作就简单了: 自定义一个重置(清空)按钮,然后触发清空按钮的事件即可:

以上是一些常用组件使用过程中需要注意的地方,针对 react 和 ant design 使用过程中出现的问题和解决方案,请参考文章:React 开发常见报错解决方法

Ant Design 使用小结的更多相关文章

  1. 阿里开源项目之Ant Design Pro

    本篇文章主要包含的内容有三个方面. 第一.Ant Design Pro简介; 第二.Ant Design Pro能做什么; 第三.初步使用; 我相信通过这三个方面的讲解能让你大概知道Ant Desig ...

  2. Puppet Openstack Mitaka Design Summit小结

    Puppet Openstack Design Summit小结 经过Puppet Openstack社区的不断努力,Puppet Openstack社区目前提供的Official Modules已经 ...

  3. Ant Design 的一个练习小Demo

    Ant Design 由蚂蚁金服团队出品, 基于 React 的组件化开发模式,封装了一套丰富而实用的 UI 组件库. 在这个练习Demo 中,按照 Ant Design 官网的教程示例,尝试使用 A ...

  4. 实现Ant Design 自定义表单组件

    Ant Design 组件提供了Input,InputNumber,Radio,Select,uplod等表单组件,但实际开发中这是不能满足需求,同时我们希望可以继续使用Form提供的验证和提示等方法 ...

  5. Ant Design UI组件

    Ant Design 是面向中台的 UI 设计语言.  http://ant.design/

  6. Ant Design Pro+Electron+electron-builder实现React应用脱离浏览器,桌面安装运行

    ant-design-pro ----> version :2.3.1 由于网上Ant Design Pro+Electron的资料太少,我就贡献一点经验   最近需要讲AntD Pro项目(以 ...

  7. button样式篇一(ant Design React)

    这篇来介绍button中elementUi.iview.ant中样式结构 ant Design react ant-react中button分两个文件less: mixins.less:根据butto ...

  8. elementUi、iview、ant Design源码button结构篇

    在看elementUI的button组件的时候,一起和iview.ant Design的button组件比 较功能.样式.代码结构,看他们的一些不同点,不同的写法哪种会好些,button的对外开放的功 ...

  9. 使用selenium操作ant design前端的页面,感觉页面没加载完

    因需要收集页面数据,遂准备使用selenium爬取瓦斯阅读页面, 瓦斯网站使用的是ant design,元素定位非常困难,页面元素都没有ID,现在还只是能做到操作登录,不能自动打开订阅,查询某公众号, ...

随机推荐

  1. error LNK2001: unresolved external symbol "public: __thiscall ControllerInterface::ControllerInterface(class QObject *)" (??0ControllerInterface@@QAE@PAVQObject@@@Z) downloadcontroller.obj

    前几天刚遇到这个问题,但是今天再碰到就又要思考怎么解决.这次特别记录一下,以防下次碰到再手足无措: 1.看到这个报错第一感觉LNK关键字,表示连接错误,这种错误有几个可以下手的点 1)函数声明和定义是 ...

  2. 「日常训练」「小专题·USACO」 Barn Repair(1-4)

    题意 之后补. 分析 这题同样也很精巧.我们不妨思考一下,如果只允许用一块木板,那么要购买多少距离?是整个的距离吗?不是,是从第一个到最后一个(哈哈哈哈哈哈哈).但是,不包括第一个的"左边& ...

  3. 【多校联合】(HDU6095)Rikka with Competition

    题意:给定$n$个数,代表$n$个选手的能量高低,现在再给一个$k$,任意在$n$个选手中挑取两个选手比赛,如果$|a_i−a_j|>K$,那么能量高的选手获胜,另一个将被淘汰,否则两个人都有机 ...

  4. Qt 解析网络数据出现ssl错误

    最近写了点小东西,哈哈, 网络部分是同学帮我搞的 在编译的时候,出现了一下错误 qt.network.ssl: QSslSocket: cannot call unresolved function ...

  5. 推荐5个机器学习Python 库,国内外评价超高

    机器学习令人无比神往,但从事这个工作的人可能并不这么想. 机器学习的工作内容往往复杂枯燥又困难——通过大量重复工作进行提升必不可少: 汇总工作流及传输渠道.设置数据源以及在内部部署和云部署的资源之间来 ...

  6. java设计模式之适配器模式以及在java中作用

    适配器作用就是讲一个接口适配到另一个接口,在Java 的I/O类库中有很多这样的需求,如将字符串数据转变成字节数据保存到文件中,将字节数据转变成流数据等. 以InputStreamReader和Out ...

  7. NO11——01背包

    # include <stdio.h> # include <stdlib.h> # include <string.h> # define max(x,y) x& ...

  8. Mac上基于hexo+GitHub搭建个人博客(一)

    原文地址: http://fanjiajia.cn/2018/11/23/Mac%E4%B8%8A%E5%9F%BA%E4%BA%8Ehexo+GitHub%E6%90%AD%E5%BB%BA%E4% ...

  9. 如何创建LocalDB数据库和数据库实例

    LocalDB是SQL Server 2012带来的新特性,它是一个专门为开发人员量身定制的轻量级数据库,下面介绍如何使用它. 创建LocalDB数据库的方法: 打开服务器资源管理器,右键点击“数据连 ...

  10. To Chromium之版本管理

    Git. 1.由于想直接submit到Chromium的官方Branch需要申请权限,目前拿不到,所以打算snapshot一个chromium版本. 本地搭建一个git的server/client,方 ...