之前在UESTC前端群里跟一些朋友分享了一些javascript的思考题,旨在促进群内技术讨论,共同进步。期间不乏思维的火花,一直想将这些宝贵的财富沉淀下来,以便日后翻阅。
目前各种MV*前端框架让人眼花缭乱,很多人大呼前端技术栈变化太快。其实不然,个人觉得这些前端技术栈其实万变不离其宗,可以一通百通,他山之石,可以攻玉。今天以class绑定语法为例,进行论述。
首先对主流的框架的class绑定进行下简单的对比。
Angular1
<div ng-class="{active: active, tab: true}"></div>
<div ng-class="['tab', {active: active}]"></div>
Angular2
<div [ngClass]="{active: active, tab: true}"></div>
<div [ngClass]="['tab', {active: active}]"></div>
Vue.js
<div v-bind:class="{active: active, tab: true}"></div>
<div v-bind:class="['tab', {active: active}]"></div>
<div :class="{active: active, tab: true}"></div>
<div :class="['tab', {active: active}]"></div>
React JSX
<div className={'tab' + (active ? ' active' : '')}></div>
avalon
<div :class="['tab', @active && 'active']"></div>
knockout
<div data-bind="css: {active: active, tab: true}"></div>
从上述对比中,我们可以看出 angular,Vue.js的语法更加简单灵活,支持数组,对象,以及数组和对象的混合,knockout仅支持对象,avalon的语法有点不便于理解,React JSX的语法需要自己拼接字符串。
个人目前在用react,每次拼接字符串这样太过低效,所以考虑封装一个支持数组,对象,以及数组和对象的混合的className生成函数。
在封装函数前,我们需要考虑好,各种边界条件。
1.数组为空或者对象的所有值均为false,应该返回空字符串。
2.只要数组中存在的一个字符串元素 则返回的className包含该元素.
3.对象中某个属性值为true,则返回的className包含该属性。
4.只有数组中某个对象元素的某个属性值为true,则返回的className包含该属性。
5.返回的className不应该有重复的class
let className = function(obj) {
let set = new Set();
if(!Array.isArray(obj)) {
obj = [obj];
}
obj.forEach(o => {
if(typeof o === 'string') {
set.add(o);
} else {
Object.keys(o).forEach(key => {
if(o[key]) {
set.add(key);
}
});
}
});
return Array.from(set.values()).join(' ');
};
如果限制输入仅为对象,那么这个函数就可以更加简单的实现了。
let className = function(obj) {
return Object.keys(obj).filter(key => obj[key]).join(' ');
};
上述方案可以用reduce实现,别有一番滋味。
let className = function(obj) {
return Object.keys(obj).reduce((next, key) => ((next && next + ' ') + (obj[key] ? key : '')), '');
};
将className和react jsx结合,就可以愉快的写react了。
<div className={className(['tab', {active: active}])}></div>
<div className={className({active: active, tab: true})}></div>
多一些思考,少一些拿来主义,日积月累。渐渐得你会发现你已经超越了你原来的仰望。