组件开发原则
遵循一定的原则,可以设计出简单易用、易于扩展的组件;
文件组织
每个(业务)组件使用的资源:(css)less、js、jsx、图片、字体等都放入一个文件夹下。
单一权责 组件的功能界限
掌握好组件拆分粒度,每一个组件要处理一个核心问题。提高重用性、灵活度,不要为了拆分而拆分,重点是满足业务需求,而不是炫技。
- 组件只关心自己的业务逻辑,一些交互,通过触发回调(
props.onClick
、props.onChange
等)的方式,交给调用者来处理;
弹框操作封装
较少字段的表单,最好可以封装到modal中,让用户在一个页面中完成添加、修改操作
- 如果弹框中的内容通用性不大,直接封装到modal中,如果后期有通用性需求,再拆分,一般情况下,modal中的内容不会有通用性,基本上都是以modal形式存在;
- 如果弹框中内容有公用情况,可以分别定义两个组件,一个是基础组件,一个是基础组件+弹框,分多个个文件定义;
- antd 中的modal有正常的声明周期,也就是willMount,didMount会在页面加载时触发一次,以后每次modal被重新打开都不触发,多个操作公用一个modal就会出现数据互相干扰;
import {Modal} from 'zk-tookit/antd'
,zk-tookit中的modal,对antd 的 modal进行了扩展,添加了beforeOpen
方法,每次打开都会触发,可以做modal的初始化工作;也避免了modal没有被打开,modal的willMout,didMount也触发,造成的一些操作浪费;
组件常用数据命名
组件只负责显示数据,至于数据由哪儿来的,外部通过props传入,一般情况下,很少在组件内发请求获取数据,但是也要视情况而定.数据名称有统一约定,可以降低沟通成本。
- 核心数据:
- 列表:dataSource;
- 对象:data;
- 基础数据:value;
- 加载:loading;
- 是否可见:visible;
- 是否禁用:disabled;
- 提交回调:onSubmit;
- 确定回调:onOk;
- 取消回调:onCancel;
- 事件回调:onXxx(onChage,onClick等等);
props类型和默认值
要申明props类型并注释属性的作用,通过props类型声明,即可知晓组件有哪些props;合理设置(不是全部设置)组件的默认props,可以有效的简化外部传入props的判断工作、以简化组件的调用传参;
1 | import React, {Component} from 'react'; |
基础业务数据组件
基础业务数据,经常以
radio
、select
、checkbox
等形式被其他业务组件调用,提供一些组件:XxxSelect、XxxCheckbox、XxxRadio、XxxTransfer等,方便其他组件调用;
设计原则:
- 组件内部发送ajax请求,获取数据,其他组件只管调用即可,不必关心数据;
- 外部提供数据,通过dataSource属性传入,一般不会用到,如果有的组件用到再扩展即可;
onLoad(dataSource, res)
回调,将ajax请求返回的数据、res暴露给其他组件,其他组件有可能会使用到数据;dataFilter(dataSource, res) => dataSource
回调,其他组件用来对ajax返回的数据进行过滤;showDisabled
属性,默认false
:是否显示禁用项,有些基础业务数据,有禁用启用操作,通过此字段表明是否显示禁用项;禁用项不可选择,label上显示禁用图标;- 修改数据时,禁用项回显问题,如果修改的数据就是禁用的,不处理会显示value,而不是label;
showAll
属性,默认false
:显示所有数据,不分禁用与启用,都是正常显示;
第三方组件再次封装
有时候,第三方组件不能满足业务需求,需要进行一次包装,包装后只是功能的增强,不要修改第三方组件原有的功能;
- 单独包装一个第三方组件,要将props也传递给第三方组件,比如zk-tookit/antd中的Modal组件,只是添加了beforeOpen回调,并将所有的props也传递给antd的modal,保持antd的modal原有功能;
- 组合封装多个第三方组件,所有第三方组件相应的声明对应的props属性,比如ListPage封装,用到了Table,listPage组件提供tableProps属性,所有表格相关的属性,都通过tableProps传递;form的getFieldDecorator所需参数,都通过decorator传递等等;
基础数据组件
比如:星期、男女、是否等硬编码数据
可以定义一个文件夹,分别定义数据以及各个组件,比如:weeks.js、WeekCheckbox.jsx、WeekRadio.jsx、WeekSelect.jsx;具体有什么组件基于需求而定,可以用到一个扩展一个。
其他
- 一些key-value相关的数据,一般定义为:
[{value: '1', label: '星期一'}, ...]
,以value
、label
为关键字。 - 组件扩展,小版本内要注意向前兼容,参数要保证只增不减,同时用
FIXME
标记好哪些是不好的设计,等到大版本更新时,进行重构; - 合理区分展示组件、容器组件;
props处理
抽取部分封装时用到的props属性,其他的原封不动赋值给html标签
1 | render(){ |
componentWillReceiveProps周期函数
获取新旧props进行对比,将props转化成内部state等操作;
判断chilren是什么组件
A.jsx1
2
3
4
5
6
7export default class A extends React.Component{
static __IS_A = true; // 添加一个静态变量,作为标记
...
render(){
...
}
}
B.jsx1
2
3
4
5
6
7
8
9
10
11
12
13export default class B extends React.Component{
render(){
const {children} = this.props;
let hasA = false;
React.Children.map(children, item => {
// 从type中获取静态变量,作为判断依据
if(item && item.type && item.type.__IS_A) hasA = true;
});
return (
<div>{children}</div>
);
}
}
C.jsx1
2
3
4
5
6
7
8
9
10
11
12
13import A from 'path/to/A';
import B from 'path/to/B';
export default class C extends React.Component{
render(){
return (
<B>
<A/>
<span>...</span>
...
</B>
);
}
}
一些实用小工具
- classNames className处理工具
- omit 浅拷贝,删除指定属性
propTypes 的使用
通用组件要声明propTypes,复杂结构(对象,数组内部包含对象)要通过PropTypes.shape明确声明内部结构
- 通过propTypes可以清晰看出组件需要哪些属性,没个属性都是什么类型、结构;
- 有些IDE可以基于propTypes给出提示;
- 方便调用;