Semantic-UI的React实现(四):基本元素组件的共通处理(父类)实现

上一篇(Semantic-UI的的共React实现(三):基本元素组件)已经提到过,基本元素组件的实现实现因为没有复杂的交互,仅仅是基本CSS类的编辑和组装,因此实现原理相对比较简单。元素

但简单的组件东西要想做的简洁,往往不简单。通处

抽象与封装

想要简洁高效地封装数十个基本组件,理父类实将组件的的共相同处理部分抽象出来是非常必要的。在ES6中js新增了class关键字(当然这只是实现一个语法糖,其背后的基本处理原理仍然是prototype那一套东西。),元素有了这个关键字js在抽象与封装的组件思想上比之前更进了一步。

当用“继承”的通处思想去考虑问题后,组件的理父类实共通处理很明显可以通过继承一个共同父类来完成(通常我更愿意用接口而非继承,无奈js学艺不精,的服务器托管不清楚接口继承如何实现)。继承以后,所有基本组件的以下处理,均可以由父类的处理完成:

编辑和组装CSS类 渲染组件本身 封装事件系统的方法回调

实现细节

编辑和组装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中,实现了三类公共功能供基本组件类调用:

编辑和组装CSS类 渲染组件本身 封装事件系统的方法回调

实现以后,基本组件类的相同处理均被抽离出来,仅剩下一些声明性质的代码。例如Header组件的实现被简化为:

import React from react; import PropsHelper from ./PropsHelper; import UiElement from ./UiElement; // 属性定义 const PROP_TYPES = PropsHelper.getDefaultPropTypes().concat([   size, sub, dividing, floated, aligned, inverted, inline, 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ㄒ)/~~)。

IT科技类资讯
上一篇:解决CNN固有缺陷, CCNN凭借单一架构,实现多项SOTA
下一篇:企业数据中心:自建还是外包?