Semantic-UI的React实现(四):基本元素组件的共通处理(父类)
|
上一篇(Semantic-UI的React实现(三):基本元素组件)已经提到过,基本元素组件的实现因为没有复杂的交互,仅仅是CSS类的编辑和组装,因此实现原理相对比较简单。 但简单的东西要想做的简洁,往往不简单。 抽象与封装想要简洁高效地封装数十个基本组件,将组件的相同处理部分抽象出来是非常必要的。在ES6中js新增了class关键字(当然这只是一个语法糖,其背后的处理原理仍然是prototype那一套东西。),有了这个关键字js在抽象与封装的思想上比之前更进了一步。 当用“继承”的思想去考虑问题后,组件的共通处理很明显可以通过继承一个共同父类来完成(通常我更愿意用接口而非继承,无奈js学艺不精,不清楚接口继承如何实现)。继承以后,所有基本组件的以下处理,均可以由父类的处理完成:
实现细节编辑和组装CSS类在系列文章二的时候有提到过,基本组件的CSS编辑和组装,在PropsHelper中实现,所有细节对外隐藏,组件仅需声明相关属性即可。如Header使用到的属性: // 属性定义 const PROP_TYPES = PropsHelper.getDefaultPropTypes().concat([ 'size','sub','dividing','floated','aligned','inverted','inline','color' ]); 这些可用属性的声明,再加上Button组件实例的props,即可编辑和组装出所需的CSS类名集合。在Header的render方法中,仅需调用: render() {
// 渲染元素
let style = this.createElementStyle(this.props,PROP_TYPES) + ' header';
return super.render(style);
}
具体的生成style的细节,在Header的父类UiElement中: /**
* 生成元素的style
*/
createElementStyle(props,propsDef) {
...
return PropsHelper.createStyle(props,propsDef) + ' ' + style;
}
渲染组件渲染组件也是共通处理实现的,作为子类的基本组件,仅需调用super.render即可: render(style,children,props) {
return React.createElement(
this.props.as,// 组件的html标签(默认div)
{
id: this.props.id,// 组件ID
className: style,// 组件class
...this.getEventCallback(),// 事件回调声明
...props // 组件其他props(用于生成class的props不需要了)
},children ? children : this.props.children
);
}
最开始的时候,其实并没有这个实现,各个组件的渲染过程还是留在组件各自的render中的。但随着组件的增多,发现这部分代码可重用性非常大。如果有特殊的组件不适用这个过程,直接在该组件中覆写该方法即可。这对整体代码的可维护性也有很大程度的提高。 事件系统的回调这个功能目前还在实现中。我的目标是,任何组件仅需声明而无需在该组件内部实现回调,由公共方法来实现回调处理。如一个Button想要用onClick方法,直接声明: <Button onClick={this.handleClick}>Btn</Button>
但在Button组件内部无需实现onClick的回调处理。(实际上也无法实现,因为Button的render处理是在其父类UiElement中实现的) const EVENT_CALLBACK = [ 'onKeyDown','onKeyPress','onKeyUp','onFocus','onBlur','onChange','onInput','onSubmit','onClick','onContextMenu','onDoubleClick','onDrag','onDragEnd','onDragEnter','onDragExit','onDragLeave','onDragOver','onDragStart','onDrop','onMouseDown','onMouseEnter','onMouseLeave','onMouseMove','onMouSEOut','onMouSEOver','onMouseUp','onSelect','onTouchCancel','onTouchEnd','onTouchMove','onTouchStart','onScroll','onWheel','onLoad','onError','onTransitionEnd','onAnimationStart','onAnimationEnd','onAnimationIteration',]; 对于事件系统的回调,在constructor中是这样定义的: constructor(props) {
super(props);
let eventProps = {};
for (let key in props) {
if (key.indexOf('on') == 0 && EVENT_CALLBACK.indexOf(key) >= 0) {
eventProps[key] = this.handleCallback.bind(this,key);
}
}
this.eventCallbacks = eventProps;
}
这个组件传入的props中如果包含'onXXX'并且这个'onXXX'在EVENT_CALLBACK中有定义,则认为该组件声明了一个事件系统的回调,那么UiElement将绑定这个回调的具体处理。处理过程如此实现: handleCallback(callback,e) {
if (this.props.callback) {
this.props.callback(e);
}
}
回顾在UiElement中,实现了三类公共功能供基本组件类调用:
实现以后,基本组件类的相同处理均被抽离出来,仅剩下一些声明性质的代码。例如Header组件的实现被简化为: import React from 'react';
import PropsHelper from './PropsHelper';
import UiElement from './UiElement';
// 属性定义
const PROP_TYPES = PropsHelper.getDefaultPropTypes().concat([
'size','color'
]);
/**
* 标题组件
*/
class Header extends UiElement {
// 类型定义
static propTypes = {
...PropsHelper.createPropTypes(PROP_TYPES)
};
// 默认值定义
static defaultProps = {
...PropsHelper.getDefaultPropsValue(PROP_TYPES)
};
/**
* 取得渲染内容
*/
render() {
// 渲染元素
let style = this.createElementStyle(this.props,PROP_TYPES) + ' header';
return super.render(style);
}
}
export default Header;
这样的好处是显而易见的:
通过这几篇,基础组件的封装处理应该说完了,接下来的几篇打算说说复杂组件的实现。在完成所有组件的封装后,还打算扩展一些复杂组件的功能(代码丑,只能多实现些功能了。总之要和官方做成不一样的/(ㄒoㄒ)/~~)。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
