天天看點

Semantic-UI的React實作(四):基本元素元件的共通處理(父類)實作

但簡單的東西要想做的簡潔,往往不簡單。

抽象與封裝

想要簡潔高效地封裝數十個基本元件,将元件的相同處理部分抽象出來是非常必要的。在es6中js新增了class關鍵字(當然這隻是一個文法糖,其背後的處理原理仍然是prototype那一套東西。),有了這個關鍵字js在抽象與封裝的思想上比之前更進了一步。

當用“繼承”的思想去考慮問題後,元件的共通處理很明顯可以通過繼承一個共同父類來完成(通常我更願意用接口而非繼承,無奈js學藝不精,不清楚接口繼承如何實作)。繼承以後,所有基本元件的以下處理,均可以由父類的處理完成:

編輯群組裝css類

渲染元件本身

封裝事件系統的方法回調

Semantic-UI的React實作(四):基本元素元件的共通處理(父類)實作

實作細節

在系列文章二的時候有提到過,基本元件的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'; 

]); 

 * 标題元件 

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ㄒ)/~~)。

作者:sheva

來源:51cto

繼續閱讀