前言
本文内容可谓是对大脑认知的一场洗礼。我们平常提到组件,就会想到重用,各个项目都能使用。而本文的组件,对于某具体项目而言是组件,但是,对于其他项目,就是个半封装的半吊子组件。面向设计、面向项目的web组件开发,就是本文要探讨的主旨。
一、人与组件
目前这个阶段,我们所使用的web组件都是人所编写的,因此,人这个个体在赋予组件生命的时候就扮演了至关重要的角色。人的技能背景,个人喜好,甚至世界观都可以从组件中得到体现。但人无完人,因此要想诞生一款完美的组件,需要上下游很多优秀的同事,把自己最棒的世界融入进去,协作完成,其中下面这4个职位参与度较高:
注意到了没有,前端开发人员处于web组件开发流程的下游;这其实没什么,大家通力协作,组件弄好,项目做好,产品做好,皆大欢喜。然而实际上,由于大学没有前端专业,因此负责团队web组件开发的,大多是从与组件几乎不相干的后台开发转过来的,或带有明显的后台开发烙印的。也就是说,前端开发不仅处于下游,还有1/3的身子在web组件之外。
一个web组件的诞生,理想状态应该是需要设计、UI工程师、前端开发通力协作完成的。有句话怎么说来着,理想很丰满,现实很骨感。
现实是:大多数web组件都是前端开发人员一个或数人,按照业界约定俗成的套路实现的,超前于UI设计,游离于UI重构。什么意思呢?比方说一个弹出层组件,有标题、关闭按钮、主内容区域,底部确定/关闭按钮。这套结构是业界约定俗成的,开发人员在写这些组件的时候,会发挥自己的聪明才智,好好地封装,好好地设计API, 目的就是可以不用关心以后设计师设计的弹出层长什么样子,因为我预留了类名接口,UI工程师换个皮肤就好了;同时能保证组件的整站复用。也就是项目开始之前,成熟、封装良好、API丰富的web组件就已存在。
亲们,读到现在,你们是不是觉得没什么问题,挺好的啊!?对的,在iPhone出现之前,大家都觉得Nokia挺好的。
从人的角度讲,这种看似牛逼的路数最大的问题就是无视了分工,所谓术业有专攻,闻道有先后,开发再逆天,也是存在局限的,或者说是人性上很难避免的东西。
1. 位置与角色
大家应该都知道,设计师和程序员处于两个不同世界的,因此,当其中一个角色独自承担另外一个角色工作的时候,自然会存在诸多阻碍,其中很重要的一点就是设计重心。
前端开发写web组件,实际上也是一种代码的设计,此时,这些程序员气质浓郁的前端开发人员设计的重点自然是放在组件功能、通用性、扩展性以及易用性上,毕竟这一块是自己擅长的,至于UI,会预留API或其他接口,也能满足常规的设计需求。
如果交互设计师或UI设计师来写web组件, UI和交互可定制则是其设计的重心,发展得好的话,可以直接成为另外一种流派,直接和(开发背景前端的)面向功能的web组件流派相抗衡。但是,国内的现状是,设计师一般不参与直接的代码建设。
实际上,从职位上划分,UI开发应该是最适应构建web组件的角色,能兼顾设计之美,重构之美,以及代码之美。只可惜由于门槛低,水平良莠不齐,尤其JS方面的驾驭能力有限,于是,最后的结果是,组件的建设都落在了工程气息浓重的开发身上。
可以看到,虽然不合理,但是情非得已。有人可能要叫了:“啰哩吧嗦,狗皮膏药涂到现在,到底哪里不合理,你到是说啊!”
OK, 稍安勿躁。由于组件这东西只能开发实现,导致在组件的设计以及使用上,前端开发有了非常大的话语权,下游决定了上游,设计师以及CSSer在组件眼中就是个摆设。比方说设计师对dialog弹框进行了某些微创新,比方说下面这样的(无标题无关闭大背景色块):
去问开发可行性,结果,开发来了一句:“哎呀,这个功能我们的弹框组件目前不支持!”我相信这种场景很多同学都遇到过吧~最后,基本上都是设计师妥协,使用传统弹框交互或布局。所以,坊间才有“苦逼的设计师”的传闻。
苦逼的设计师这样苦逼地工作了很多年,还好,大家就这么过来了。然而,事物是不断发展的,技术是不断进步的,随着CSS3, SVG等现代web技术日趋成熟,我们在UI展现层能够做的事件就非常多,更新变化也更加快。在这种大环境背景下,还让开发来决定设计,显然这对产品是不负责任的。其他竞品在组件UI细节上不断闪现人性化、情感化的创新之处,交互也更加流畅与舒适;而你还抱着“我们这是成熟的弹框组件,不能随便改”的想法固步自封,使用”duang duang duang”生硬,机械,呆板的弹框组件,注定在现代web浪潮中被冲到沙滩上。
对于页面工程师,切图仔而言,其实也挺苦逼的。在部门没啥地位的页面仔们,苦学了一种新的布局,比传统实现少嵌套2层标签,且满足各种场景需求,好棒!结果交接的开发来了句“我们的选项卡组件不支持你这种结构,你需要调整下”。
具体项目的具体组件本来就应该是大家通力协作完成的,现在就因“组件是我写的,我就是发言人”的心态限制设计、重构的创造力。这就是不合理之处。
2. 扬长避短
组件的作用是什么呢?就是“偷懒”,来个新页面,“啪”一套;来个新项目,也是“啪”一套。功能棒棒哒,任务完成,老婆再也不用担心我加班晚归啦!
偷什么懒呢?主要是偷自己不擅长那块的懒。这是人之常情。设计师不懂代码,他希望的组件是,直接可视化操作就OK的;页面制作人员,他希望的组件是JS功能那块它不需要操心;前端开发们,则希望组件HTML布局那块不需要关心。
好了,现在组件都是开发背景的前端写的,所以呢,很多HTML布局啊,样式表现啊,都集成在了web组件中,一些动态的表现就使用洋洋洒洒的API控制。于是,开发使用的时候,只要按照特定的套路,自己不需要去写页面之类的,组件就棒棒哒了!
这样的例子很多的,比方说知名开源项目kissy中的选项卡组件,根据我的观察,其选项卡要么是要通过JS脚本动态创建,任何特异化的需求都是通过丰富的API接口或者回调实现的。总之,组件的使用只需要玩弄JS, 不需要把玩HTML; 要么就是套用固定的HTML结构以及className值。这比较符合JS造诣很深的前端开发的偷懒模式。
等等,篇幅原因,就不一一举例。
这并没有什么对错之分。有的企业就是功能为王,业务导向;有的企业就是产品为王,体验优先。不同的企业文化,不同的产品要求,不同的团队规模与技术能力,决定了web组件的表现形态、使用方式等。只要符合企业的生产与成长需要,都是很棒的web组件。因此,类似kissy这样的模块集合,是否适用于单兵作战的中小企业以及注重用户体验的创业团队,就需要执行者好好斟酌斟酌了!说不定,jQuery UI可能更合适一点。
至少对于我而言,绝不会去使用对HTML做过多限制的web组件的(例如要求节点必须如何如何…):
KISSY 1.4 srcNode 初始化组件时必须要求内容节点必须包含类名 ks-overlay-content (这里 ks- 为 prefixCls)…
“扬长避短”原本是个褒义词,显然,我放在这里讲,是要说其不好的。有句话讲的好,叫做术业有专攻。前端开发都会HTML/CSS, 毕竟相比程序开发,门槛很低,在相关领域也浸染了些年月,构建web组件的时候,感觉HTML/CSS方面的应用也是比较顺手的,功能也都能跑起来。但是,在HTML/CSS方面造诣很深的开发看来,前端开发web组件的HTML结构等设计不敢恭维。举个真实的例子:
要实现一个相对文本框定位的Autocomplete组件(相比绝对定位, resize
时候无需JS重定位),前端开发是这么实现的,在<input>
文本框外面wrap包裹了一层<div>
标签,设置position
为relative
, 然后Autocomplete主列表容器就在这个包裹<div>
里面,绝对定位于该<div>
, left/top值与文本框尺寸关联。
最后的实现,效果是有的,兼容性也是可以的,在前端开发看来应该是OK的,自我满意的。但是,在我看来是相当糟糕的!
这外面包裹的一层<div>
标签完全是多余的,我们无需包括标签就能实现我们想要的相对文本框定位效果,而且兼容IE6+. 目前的实现,这个包裹的<div>
看上去貌似起到了辅助剂的作用,实际上留下了超级隐患,让组件生命力(适用性)立马降低50%。
小隐患是,可能相邻父子选择器会失效,比方说.parent > input
选择器,会因为莫名其妙爸爸变爷爷导致文本框样式失效,这个问题嘛,改改CSS就可以了;以及多了一个relative
, 元素的z-index
层级管理又更复杂了一步,维护成本增加了!
超级大隐患是,Autocomplete组件下拉框高度超出容器高度是很常见的,但是,由于你外层有了一个position:relative
的父级,只要外面任意一个容器有overflow:hidden
,你的Autocomplete下拉就会被拦腰斩断,部分内容不可见;如果是overflow:auto/scroll
,则会出现讨厌的滚动条。这个组件基本上就废了!但是,如果没有外面这层讨厌的position:relative
父级,overflow:hidden
就干不掉下拉列表,Autocomplete组件就能长青不老,适用性提升了整整一个量级。
上面截图,仔细看会发现有个*-placeholder
的<label>
元素,先不说这个label
元素没有for
属性,要知道,企业产品近8成用户的浏览器都支持原生placeholder
(包括颜色等自定义),对于这部分浏览器,我实在想不出任何需要自定义实现placeholder
效果的理由。最大的可能性是前端开发童鞋不知道目前凡是支持placeholder
属性的浏览器都可以CSS自定义吧。也就是前端开发本身的技术深度以及视野的局限。
我们对上面的絮叨总结下,就是想表达,人的局限性带来了web组件的局限性,唯有相互协作,告别单职挑大梁,才能让web组件有所突破。但是,目前这个阶段,这现实吗?优秀的前端如此匮乏,尤其能够上下游承接的,想寻求突破,我们是不是可以从别的地方入手?
二、组件的抽象与封装
我们在写web组件的时候,我们总是会尽可能地去抽象功能,封装API。 于是,当我们使用这些组件的时候,我们只需要针对一些常用API去做设置,就能满足我们日常的功能。
在很长一段时间里,我一直都是这么做的,业界几乎所有开源的web组件,包括团队内的web组件都是这样的设计思路。
然而,随着时间推移,在各种类型的项目中浸泡,我发现,尽可能地去通过JS的手段封装组件似乎不是未来的趋势,反而可能会成为一种制约。
1. 现代web技术发展
CSS3/HTML5等现代web技术不断发展,以及IE6浏览器的淘汰。浏览器自身能够完成的事情要比以前多很多很多。举个极端的例子(都是厂内现有的真实案例),模拟单复选框的web组件,如果按照传统的组件开发模式,API这块(events回调API缺省)可能就会是下面这样子:
从代码的角度讲,还是很美的,对吧;功能上也是可以的,各类需求也能满足;API的抽象与封装也是没有不足。只是,恕我愚钝,这洋洋洒洒600+行的JS代码的价值在哪里?在IE9+浏览器下,单复选框的自定义效果是不需要一丁点一丝丝JS代码的,对于IE7-IE8浏览器,我们只需要关心input
本身,全局委托click
, 让单复选框input
的类名和checked
状态保持一致就可以了,其他的交给CSS选择器完成就好了。全部的代码量跟上面截图API参数代码量基本上一样,而且对设计师和页面重构人员更加友好。
工程化的思维方式,高度的逻辑思维与抽象能力,以及造物者的美妙感觉,似乎让开发热衷于设计与封装API, 熟不知,当新技术出现,或者发现有其他更简单的实现的时候。那些所谓的封装是显得那么的笨重与鸡肋。
“好了,你别说了,我改还不行吗?”组件这东西,由小变大很简单,可以向前兼容;你想从大变小,我只能呵呵。还是等下一个全新的版本,或者其他完全不相干的项目了,兄弟!
2. 变幻莫测的UI层
现代web技术的发展,让我们在UI表现上可以有更多的选择。这对web组件UI这块的抽象与封装带来了很大的挑战,来看看一些同行的言论吧:
如果有组件自信对UI层进行了完美地抽象与封装,这表明,这个组件已经对UI层的表现有了很大的限制。这是毋庸置疑的,有些事物本身就是对立的矛盾体,工程化就是要讲求一致性,但是,个性化显然就是需要不一致,而UI表现就是一件很个性化的事情。So, …
当前的web组件面对新项目新的UI表现需求,采用的做法要么就是new
一个新的封装进行二次封装,要么多些写代码做冗余处理。恩,都不是什么好做法,不过又没有办法。
3. 跨部门的合作
最新,我们完成了一个项目。这个项目质量非常高,无论是UI, 交互和体验,各方的评价也很好。后来我们要开始一个新的且比较大的项目,就希望把已有项目很多好的东西借鉴过来。设计还是同样的一批设计师,但是,前端团队却换了一拨人。理想的状态应该是这样的,新项目的前端团队,直接使用之前项目这边的前端UI组件(除了颜色,尺寸什么的都是一模一样的),less的变量文件颜色一改,分分钟无缝转移,多棒啊!
但是,最后的结果是,新的前端团队放弃了之前项目的前端解决方案,还是使用了自己的简洁派做法,seajs + jQuery + …
各位观众可能会疑问了,咦,为啥不使用现成的东西啊?主要原因就是上手成本和学习成本,企业这边的前端组件体系可以和kissy一拼了,面向对象、模块化、按需加载,API封装也是尽善尽美,走Grunt.js玩nodejs, 前后端分离,走得还挺超前的。但是,自己团队内部新人有人带,有人教,可以慢慢上手。但是,你这一套东西交给其他团队,考虑到其量级以及复杂度,以及项目的紧急程度;一看到你那洋洋洒洒N多的API参数,着实让人望而生畏。以及还有不可忽略的代码风格、模式等差异。
为什么会有那么多API?
这个问题很有意思:“为什么会有那么多API?” 这个问题可谓是问到点子上了。原本组件诞生的时候,API是没有这么多的,后来,现有的API不能满足某些UI层或交互层的使用场景,于是,前端开发就抽象一下,新增一个API, 满足这个需求。但是呢,这项目啊是一个接一个,需求呢,也是千变万化。没过多久,又来了个需求,开发据理力争,还是没能挡掉,结果,又在组件里新增一个API。 久而久之,就有了现在看到了很多API参数,此时,这个web组件就可以称为“成熟的web组件”,对外宣传“适合多种应用场景”。这其实很有意思,所谓的适应性居然是通过增加各种API,增加组件代码量实现的,然后还自我得意一番。我开始有些明白为啥小小的单选框模拟组件居然需要600多行的JS代码了。
归根结底,还是“尽可能对组件API进行抽象和封装”这样组件编写意识导致的,这种想法和意识实际上已经不符合发展的趋势了。
我们看来有必要转换思维。
三、转换思维,分离与半封装
大家都知道YUI已经停止维护有段时间,至于原因,不知道大家有没有好好体味下(参见下图言论):
正如我所言,为了满足UI需求,讲求封装的设计理念,必然会导致web组件越来越大,越来越臃肿;同时,前端开发这个角色由于关注点和职位跨度原因,对于UI层的处理能力和其对JS语言本身的处理能力还是有不小差距的。
因此,考虑到未来发展,我们必须做出适当的改变。我个人觉得可以从这两方面寻求改变:分离和半封装。
1. 分离
实际上,目前的UI组件也是有分离的,很多样式都可以通过类名控制。然而,这些分离都是逼不得已的,比方说Autocomplete的列表样式,你总不可能每个列表都内联一段样式相同的style
设置吧。但是,如果条件允许,样式控制都是尽可能在JS中完成的,例如Autocomplete的列表容器的尺寸、定位、层级等等;甚至有些组件直接内嵌完整的CSS代码。
这样的设计是奔着足够封装,调用方便去的。但是对UI的友好程度,恐怕就不是设计者的关注点了。
显然,这里要讲的“分离”要比传统的“不得已分离”要更进一步。包括两方面:
①. 样式控制从JS分离
即只要是静态样式控制,全部交给外部的CSS来完成。甚至包括组件的状态控制(尤其是只要支持IE9+的项目),例如显示,隐藏等等。
拿经典的弹框组件举例。至于absolute定位,还是fixed定位,背景色是黑色还是其他,透明度多少,弹框是否居中定位等等,组件都不管(目前这类全都JS API控制),组件要关注的是功能层面的东西。千万不要自己为是,使用JS计算让弹框居中显示(外带resize重计算)。UI需求不是你可控的,比方说我们这边几个项目的弹框的需求是上下2:3滚动跟随显示,CSS完全可以实现此效果,CSS才是最好的UI样式API。
说到这里,忍不住爆料下,由于各种错综复杂的原因,项目的弹框组件Overlay的透明度和颜色API目前是个酱油,自定义没效果。于是,最后还是使用CSS的!important
重置了JS的style
控制。如果当初全都交给CSS, 什么事都没有,还省了一大波JS代码,呵呵呵!
②. 参数来源从JS分离
举个最简单的例子,文本框/文本域的placeholder
占位符效果组件,其占位符内容通过JS的{ placeholder: '' }
参数赋值吗?很显然,要优先使用HTML元素上对应的属性值。这其实是很基本的常识,然而,很多组件只有JS API这一个入口。
或者tips组件的提示内容源自元素的title
属性等。
即符合语义,页面工程师还可控。一举多得。
以上的分离,省JS代码是小,更大的意义在于释放了UI设计师以及UI工程师的潜力,使上下游所有的成员角色都参与到web组件的建设中来,只要这个设计师的设计能力>开发,页面重构人员的重构能力>开发,这个组件的品质,绝对就会比前端开发一个人完成的质量要高。而且,对于其他不同UI风格的项目具有更强的适应性。
2. 半封装
一提到web组件,我们很自然跟封装联系在一起。对啊,你不封装怎么叫做组件呢?现在,我们有必要转换思维了,要想满足日益高涨的UI层需求,同时组件不会日趋臃肿,我们就需要对组件进行半封装。
那问题来了,什么是“半封装”呢?
所谓“半封装”是指我们只封装语言层面以及功能层面的东西;对于UI层,虽然也是JS, 但我们按照CSS的思维处理,以满足当前项目UI需求为主,不讲求封装以及API设计,保持随时的激活态,无论是UI工程师或者前端开发都可以根据具体项目具体需求调整之。
需要注意的是,此“半封装”是针对不同设计风格的项目而言,对于某一个具体项目,其web组件还是完全封装的,还是有成熟的API接口的,小白开发也是可以直接使用的,只是不能直接应用到其他项目!
分离,和半封装使用示意图表示就是:
对应的职位参与如下:
从上图可以看出,我在第一章节大肆分析的当前web组件分工不合理性(见下图gif)得到了很好地解决。web组件样式控制CSS化,以及非封装的HTML结构、类名等大大提高了偏设计的前端对组件建设的参与度,对组件设计品质的提升,非常有帮助。
关于web组件的半封装性
对于开发思想浓郁的程序员,可能有些无法接受这所谓的“半封装”思想。我cow, 每来一个项目,都要对组件进行一番修整,人力成本怎么算?如果支援10个项目,就有10个子组件,日后维护怎么办?
关于人力成本:
传统的web组件是由前端开发在原组件基础上二次封装成项目需要的web组件,这个成本,跟直接动组件代码中的小部分UI层内容,实际上没什么区别的,这个下一章节再次提到;
关于维护成本:
首先对于每一个具体项目而言,组件都是通用的,而半封装,只是对其他项目而言是半封装,对于具体项目依然是个全封装的web组件,跟传统web组件维护成本是一模一样的;
其次,半封装的web组件,非封装的部分是多变的UI层,整体结构依然是面向对象开发,其他部分依然使模块化加载,因此,看上去10个子组件,实际上仅仅是10个跟实际项目紧密契合的UI布局相关的子组件。要知道,UI层本来就应该具有特异性,独立开来反而降低了样式调整带来冲突的可能性。
最后,对于不同项目而言,组件本身就应该是独立不耦合、相互无关联的。比方说jQuery, 10个项目都使用jQuery, 有个项目需要使用jQuery某新特性,于是升级都爱了2.0, 其他项目也要更着升级吗?显然是不推荐的,对吧,新版本的浏览器兼容性可能不一样,一些老的API可能删除了。jQuery况且如此,我们实际开发时候,10个项目实际上就是10个组件体系,别看貌似代码都是一样的,维护起来还是要独立维护。
因此,综上所述,根本就不存在增加日后的维护成本。
半封装web组件唯一的问题在于,对小白从业人员不友好。很多新人就是大自然的代码搬运工,就是希望组件足够傻瓜化,最好直接引入一个JS文件,自己想要的功能就出现了。这就是为何我们平时在互联网上看到的开源的web组件都是封装很良好的原因,你使用麻烦,哪个小白会使用!同时,因为需要考虑各种应用场景,API很多,组件很重,为了宣传,只能美名曰强大、适用于各种场景。正如无招胜有招,没有使用场景才是最能适用于各种场景。
我们在企业打工吃饭,首先考虑的应该是组件本身的质量、产品和项目的质量,至于开源什么的,是不是想太远了。
我们有专业且可爱的同事们,为何要把合作的同事当小白,而不是不发挥各自的特长,一起把产品弄得棒棒哒!?
举例补刀
就在我写到这个位置的时候,高级视觉设计师J向我咨询日期时间选择组件的时候,出现了这么一句话:
网上能够找到的日期时间选择web组件都是封装良好的web组件,对于对设计没要求的小白用户或者一些小厂很友好,但是,对于对设计很重视的使用场景,所谓的“完全封装”反而成为了一种制约,且不说组件的class类名风格是否符合团队规范。
其实,我们希望模块化或者组件化的东西是日期筛选或者排列的算法。对于UI层,你大肆集成,一了百了。最后的结果呢?被设计师嫌弃了!其实呢,里面的HTML构建也被HTML工程师嫌弃了!后来,设计师委曲求全接受了一款时间选择器组件,结果还不支持IE7浏览器,真是造化弄人啊!
正在阅读的你,遇到这种情况你会怎么办?试想下,如果我们有一个半封装的web组件,UI层内容和模块层内容清晰且独立,此时,UI工程师就可以专心捣鼓UI层内容,可以满足设计师任何挑剔的需求,岂不美哉?!
四、面向设计高级定制的产品组件
怎样的产品才高端,有档次,脱颖而出?
我们可以拿衣服为样板,回答上面这个问题。
很显然,私人定制的衣服要比流水线上生产的衣服,要更有档次,产品品质要更高。
完全封装的web组件,就好比流水线上的衣服,讲求工业化,效率以及成本。但是,这样的web组件做出来的产品的档次和质量,未免有粗糙之感。而本文所反复推崇的半封装的web组件,非常重视量体裁衣的设计、量身定制的交互,使得我们的web站点犹如私人订制的高级礼服。在芸芸产品中脱颖而出,赢得口碑。
需要明白的是,私人订制并意味着完全放弃工业化、模板化的东西。就算你让玉兰为你设计高档礼服,礼服的制作过程中,还是会用到线稿、样板等等。只是并非完全的流水化。面向设计的半封装的web组件也是这么回事,我们还是需要面向对象、需要模块化加载、通用的事件处理等等,只是这些成为了背后的一部分而不是全部。
下面我们一起来看一个很有意思的问题,初期开发成本问题。
面对设计的要求,我们以前的做法是这样的,web组件不动,针对具体项目,common.js
中来个二次封装;而现在的做法是采用私人定制的方法,在原web组件根据设计需求,捣鼓UI相关内容。差异见下示意图:
都是需要为了满足UI的需求做一些工作的,所以前期工作成本并不好评估。但是,可以肯定的是,JS的代码量下降了很多,且对于设计对于UI的定制性更强了,如果团队有很多厉害的设计师,这个web组件的品质就牛大了。
个性化设计与工业化生产的矛盾
每个企业应该都有自己重视的产品,而且都希望这个产品各方面品质都高人一等,对,是各方面品质,这样才有竞争力,我们腾讯更是看重产品品质。
我们有很多顶尖的产品经理和交互/视觉设计师,设计的web控件既美观又大气、而且还亲近用户,细节上很多微创新,高品质的产品有个产出的前提,但也只限于前提,最后的呈现还是需要开发来实现的。
可见,开发同学在产品品质的把握上是多么的重要。
每个IT企业里面都有一个或一群开发,他们希望自己的代码可以改变世界,同时吸引软萌妹子;他们希望通过代码生产工具以及类似可复用的东西,解放生产力,提高效率;他们希望自己的代码功能棒棒无bug, 性能卓越无匹敌。
正如之前讲过的,既是优点也是局限。重复利用的天性刻在了骨子里,虽然中心明白设计很重要,但是,对于设计效果的实现,总会让设计师苦笑不得,为何?
因为这本身就是矛盾的,优秀的开发讲求代码复用、性能,然而个性化的设计就是要与众不同,避免复用。所以,各位开发同学,如果你的团队设计很优秀(如果设计跟你一个水平,忽略这句话), 为了产品的高品质产出,我们必须有强烈的面向设计的开发思想,首先第一点就是心理上要能够接受,各个项目之间基本上没法重用的web组件;其次,行动上,愿意花一些额外的功夫和心思,雕琢设计的细节实现。
等最终高品质的产品出来了,内心会很充实会自豪,你会发现自己有了长足的成长,设计师MM似乎对你更友善,跟你说的话也比以前多了。
举例与补刀
就在上个月刚刚完成了一个取色器的定制,这里来分享下哪些是需要高级定制的,哪些是按照模块化使用的?首先下面这个是设计图:
首先,这个取色器特异性非常强,网上绝对找不出第二个取色器组件长这个样子。所以,崇尚自然搬运工的,可以洗洗先睡了。虽然网上有很多取色器组件,我看过他们的实现,不得不说,真不怎么样(直接搞一张渐变图片是要闹怎样)。所以,为了还原设计品质和保证组件代码质量,反而是高级定制来得简单快捷。
首先,我们要分离UI内容和组件模块内容。模块内容包括:Dropdown组件(含边界判断),颜色转换模块(HSL, RGB, HEX格式间颜色转换);剩下就是UI层内容,这是需要私人定制的。没错,这个组件的定制工作量比一般的组件要大很多。从代码量来看,要五五开。
再次强调下,大家一定要摒弃“web组件完成后尽量不要动里面代码”这样过时的想法,为了满足设计,额外的成本付出还是需要的,只是以前只在外面小打小闹,现在是内部调整。
至于如何具体分工,因团队结构个每个人擅长的领域不同而不同。
好,这里,有必要加粗说一下:恰如礼服定制需要技术娴熟的制衣工人一样,web组件如果想走面向设计的高级定制策略,团队里一定要有类似的技术娴熟的前端人员,否则会有相当高昂不划算的定制成本。
五、实践出真知
近2个月之后……
前面提到过设计师jerry对日期时间选择组件的咨询,后来,莫名飞来一些蝴蝶,这个日期时间选择组件就我来实现了,基于面向设计的半封装理念,我是怎么实现的呢?
- API分离 – 回归UI组件的本源 – HTML
大家都知道,HTML5里面,本身就是有类似date
,datetime
类型的input
控件的,如Chrome浏览器下会自动调用自己的时间选择控件:所谓UI组件,无非就是实现满足产品气质的统一兼容的浏览器的那套东西。道家讲求追本溯源,从这个哲学层面讲,使用浏览器以及HTML5规范那套交互体验,一定是最佳实践。传统的洋洋洒洒的JS和五花八门的API都是逆时代而行,注定要成为历史的。因此,我这里实现的日期时间选择组件基于原生的规范的HTML5input
元素实现,没错,只能是input
元素。具体包括:<input type="date"> <input type="year"> <input type="month"> <input type="hour"> <input type="time"> <input type="minute"> <input type="date-range">
每种
type
列表对应一种时间选择面板。例如,选年的,和选月的(终效果截图):不仅仅如此,可选时间范围,甚至包括时间选择的跨度均是通过HTML原生属性设置完成。包括:
min
,max
,step
等。<input type="date" min="2015-07-03" max="2015-07-28"> <input type="hour" value="09:20" min="8" max="20" step="2">
min
对应于传统日期组件的startDate
API,max
对应于endDate
,value
对应于initDate
。而这里,我们将类别交给
type
, 范围交给min
/max
, 从JS分离出来,交给了HTML. 即增加了组件的可读性和可访问性,同时,给我们的组件瘦身,可以专注于呈现本身。最终,我们的组件API就非常的简练,只需要一个
onShow()
/onHide()
回调,使用也更加规范(因为遵循的HTML5规范),记忆与学习成本也更低。最最厉害之处在于,我们只要在common.js
全局实例化一下,例如:$('input').each(function() { var type = $(this).attr('type'); if (/^date|year|month|time|hour|minute|date-range$/.test(type)) { new DateTime($(this)); } });
其他所有子页面,你都不需要额外的JS, 设置额外的API。 只要按照HTML5规范,写好HTML内容,日期时间选择功能就杠杠地呈现在那里。
工作不要太轻松哦!
什么?选择日期的回调?
用户只要关心
input
元素本身就好了,就跟使用原生的input
元素一样,给其绑定change
事件,当文本框中日期变化的时候,change
事件就会自动触发。可以说,就算没有这个UI组件,我们的页面功能也是可以正常使用,这就是API分离给HTML在可访问性上的质的提升。 - 面向UI设计的高级定制
UI层的东西变化性非常强,你不可能说是写了个组件,适用于所有团队,所有场景,那你的组件该有多大,多少逻辑判断哈!对于某一个项目而言,是不需要的,我们需要稳固的核心,以及在这个基础上,面向UI的高级定制。日期时间选择比较难实现的一个面板就是日期范围选择,类似这样:其实呢,要实现有此功能并且功能通用的组件,并不难,我们从网上找一下,也是可以找到的。但是呢,如果你看了设计师对选中细节的处理,你去找个可以满足如此UI设计的组件看看,就很难了!那些细节呢?包括:- 独立选中真空;
- 独立选中真空;
- 非首尾选中实色;
- 首尾选中半直角半圆角;
- 首尾选中处于边缘且折行则顶到边缘且半开放;
- 首尾选中处于边缘不折行则不顶到边缘;
- 正常区域方方正正,选中顶到边缘。
- …
咋整?显然,面对如此UI,我们需要对组件内部实现进行简单的定制,主要定制内容包括:
- 通常态类名
- 选中态类名
- 选中起始类名
- 选中结束类名
- 月初类名
- 月末类名
- 星期几对应列类名
如果我们是一个普通的日期选择器,只要两个类名就可以了:.ui_date_item
和.selected
。但是,当我们面对的UI组件的样式非常精致的时候,我们就必须根据设计量量身定制一些类名,辅助实现我们的表现层效果。这样的成本是必须也是必要的。
提问?我们还需要对国家法定节假日(包括周六周日)做处理吗?
从组件本身强大角度讲,支持自然显得更牛,但实际上这只是开发人员自己的感觉,现代组件讲求轻便灵活,如果项目没有这样的需求,我们就没有必要做类似的处理;如果后期提出了需求,我们再处理之。
这种思维方式有别于传统的——“我需要考虑好各种使用场景,弄一个功能丰富强大的组件”;而是“UI层需要什么,我代码就做什么,尽量减少API,只考虑常用场景,生僻场景置之。”
组件体系通用的定位模块。时间选择器内部每种类别的面板也是独立模块,分独立调用也可以从其他面板切换调用。
六、最后的大总结
- 为了最大化组件的品质,组件的实现要讲求分工;
- 要想分工,需要将组件UI层东西从JS剥离出来,发挥CSS/HTML工程师的在UI处理上造诣;
- 想要剥离,就要求组件半封装,避开大而全的API;
- 所谓半封装是功能封装,UI不封装;但对于某一具体项目,组件还是完全封装的;
- 避开API的方法包括交给原生的HTML属性API, 以及根据项目UI场景设计API,告别无意义的生僻场景API的使用。
- 发挥CSS的潜力,尽量避免使用JS做一些自己为是的功能;
- 面向设计的组件构建思想只适用于战略级项目、或希望成为精品的项目,以及需要配备优秀设计和UI开发。否则传统的大而全的组件反而更适用。
- UI组件的本质是HTML和CSS, JS只是辅助,千万不要乱了主次。
最后,我们得到的就是一款UI设计精良,同时代码轻便简洁,量身定制的高级web组件,配合模块化的管理,整个项目的品质就可以有质的提升。
OK, 以上就是自己对web组件的一些思考和感悟,可能讲得有些啰嗦(概要版点这里),希望不会影响中心思想的传达。另外,大家读完之后有什么想法,或者本身就对web组件有一些思考,都欢迎留言讨论。
最后,感谢阅读!