Semantic-UI的React實(shí)現(xiàn)(二):CSS類構(gòu)造模塊
更簡(jiǎn)單的類名標(biāo)簽
Semantic-UI使用了更簡(jiǎn)單的類名聲明。用過Bootstrap的同學(xué)都會(huì)被其復(fù)雜的類名標(biāo)簽折磨過,例如一個(gè)簡(jiǎn)單的按鍵樣式,不論顏色或是大小,都需要btn-前綴聲明:
- <button type="button" class="btn btn-primary btn-lg active">Primary button</button>
在Semantic-UI中,類名更接近自然表述:
- <button class="ui blue large active button">Blue Button</button>
語(yǔ)義化的樣式聲明
樣式名并不是對(duì)某種組件進(jìn)行的類型聲明,可以通用到其他組件中。例如對(duì)于Label(標(biāo)簽)組件,也可用與button相同的CSS類聲明其樣式:
- <div class="ui blue large active label">Blue Label</div>
這樣的好處是顯而易見的,CSS類名語(yǔ)義化,剛方便使用和學(xué)習(xí)。
類名構(gòu)造模塊的實(shí)現(xiàn)
從以上細(xì)節(jié)可以看出,每個(gè)組件的類聲明均可由公用模塊生成,每個(gè)組件僅僅聲明本模塊可使用的Props即可。以Button舉例如下:
- import PropHelper from './PropHelper';
- import UiElement from './UiElement';
- ...
- let PROP_TYPES = ['primary', 'size', 'color', 'basic', 'active', ...];
- class Button extends UiElement {
- static propTypes = {
- ...PropHelper.createPropTypes(PROP_TYPES)
- };
- render() {
- let style = this.createElementStyle(this.props, PROP_TYPES, 'button');
- return (
- <div id={this.props.id} className={style} tabIndex='0'>
- {this.props.children}
- </div>
- );
- }
- ...
- }
Button類聲明了其可以使用的class類名,通過共通處理生成style即可。生成style的共同處理,由PropsHelper類負(fù)責(zé)完成。
PropsHelper
PropsHelper類主要的職責(zé)有兩個(gè):
- 生成各組件所需的class類集合
- 生成各組件的props屬性檢查定義
PropsHelper作為工具類,相關(guān)處理過程中并無狀態(tài)參與,方法應(yīng)該聲明為靜態(tài)方法(static)。
props屬性檢查
Semantci-UI中的所有class類屬性的集合是可枚舉的,這些屬性直接在PropsHelper中定義即可:
- const BOOL_PROPS = ['ui', 'active', 'disabled', 'circular', ...];
- const STRING_PROPS = ['icon', 'appendClass', ...],
- const NUMBER_PROPS = ['column', 'wide', ...],
- const COLLECTION_PROPS = ['color', 'size', 'position', ...];
對(duì)于每個(gè)組件的屬性檢查定義,可以遍歷傳入的屬性,并根據(jù)名字找到該屬性的PropTypes定義。
- class PropsHelper {
- ...
- /**
- * 生成屬性檢查
- */
- static createPropTypes(propTypes) {
- let result = {};
- propTypes.forEach(function(typeName, index) {
- if (BOOL_PROPS.indexOf(typeName) >= 0) {
- result[typeName] = React.PropTypes.bool;
- }
- else if (STRING_PROPS.indexOf(typeName) >= 0) {
- result[typeName] = React.PropTypes.string;
- }
- else if (NUMBER_PROPS.indexOf(typeName) >= 0) {
- result[typeName] = React.PropTypes.number;
- }
- else if (COLLECTION_PROPS.indexOf(typeName) >= 0) {
- result[typeName] = React.PropTypes.oneOf(PROPS_VALUES[typeName]);
- }
- });
- return result;
- }
- }
class類集合組裝
與createPropTypes同樣的思路,將傳入的組件props遍歷一遍,找到各自prop屬性的類型定義,根據(jù)類型定義編輯和組裝該組件的class類集合。
- class PropsHelper {
- ...
- /**
- * 根據(jù)屬性生成引用的class
- */
- static createStyle(props, types) {
- let style = '';
- for (let i = 0; i < types.length; i++) {
- let type = types[i];
- if (props.hasOwnProperty(type)) {
- style += this.formatStyleValue(props[type], type);
- }
- }
- return style;
- }
- /**
- * 格式化屬性對(duì)應(yīng)的class
- */
- static formatStyleValue(value, type) {
- // 如果是數(shù)字型屬性
- if (NUMBER_PROPS.indexOf(type) >= 0) {
- return ' ' + this.getNumberStr(value) + ' ' + type;
- }
- else if (COLLECTION_PROPS.indexOf(type) >= 0) {
- if (type == 'size') return ' ' + value;
- return ' ' + value + ' ' + type;
- }
- else if (BOOL_PROPS.indexOf(type) >= 0) {
- if (!value) return '';
- if (type == 'imaged') return ' image';
- if (type == 'iconed') return ' icon';
- if (type == 'long') return ' long scrolling';
- if (type == 'equalWidth') return '';
- return ' ' + type;
- }
- else if (STRING_PROPS.indexOf(type) >= 0) {
- return ' ' + value;
- }
- else {
- return '';
- }
- }
- }
這樣實(shí)現(xiàn)以后,各組件在各自屬性的定義和class類聲明的處理時(shí)獲得了兩方面的益處:
- 屬性統(tǒng)一管理,不用再各自聲明
- 代碼復(fù)用性和耦合性更佳(特別是在復(fù)雜組件的使用中)