a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 146|回复: 0

[专业语言] Java认证辅导之构建前端UI组件的新思路

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
Java认证辅导之构建前端UI组件的新思路, Z4 n8 h, M/ l8 N
构建前端UI组件的新思路
4 W* ?. ?6 _/ z/ F$ V前端UI组件,目前流行的实现方式大多源自传统客户端的UI设计体系。无论是早期的Bindows,还是近几年兴盛的ExtJS,其UI组件都在模仿客户端软件,代码实现建立在复杂的继承体系上。好处是可以构建出和客户端体验一致的一整套UI组件,但弊端也很明显:组件长得都差不多,代码则继承太深,牵三挂四,不够轻便。
! a- }5 \  @) O: W. Z, }$ u% N如何才能让前端UI组件轻便灵活起来呢?首先得意识到Web UI设计有自己的独特性。Web页面可分为两种:一种是以展现信息为主的Web页面(web page),另一种是以操作信息为主的Web应用(web app)。对复杂的Web应用来说,可以采用ExtJS等类库来构建类客户端体验。但是,越来越多的Web应用已逐步脱离模仿客户端的阶段,开始从Web 的独特性出发,将传统UI组件的功能融入到Web页面中。3 @$ t) Y! n1 d8 \( j
举一个例子:国内的Web邮箱,无论是163还是QQ邮箱,都会让人联想起Outlook或Foxmail等客户端软件。这种模仿成本很高,然而带来的效果个人觉得却不是很好。反观Gmail,整体UI设计,简明轻快,但功能一点也不逊色。在Gmail里,看不到Tree,看不到DataGrid,脱离了这些传统UI组件的框框,将功能融入到Web元素里,反而让用户更自然、更高效。- n2 J5 a/ [% Y8 l2 x9 M) Z" g
前端UI组件的Web特性,需要我们打破传统思维,换一个角度,重新去思考UI组件的基本组成要素。换什么角度去思考?什么角度才是合适的?这离不开具体场景。下面以一个实例来说明。
1 q0 B: B" z. {7 ?在淘宝页面中,以下是几个常见的UI组件:
0 c# V4 p6 S! U" W传统思路里,我们会构建三个组件来实现:
2 U' d( g5 ^2 f# e7 D1 wSlide(轮播)组件
: Q4 R7 a4 N8 Q" G& \3 y' K% B. kTabs(标签页)组件& }" T; e. w, x$ W/ |
Carousel(旋转木马)组件
" l7 ?: W8 A/ l+ C3 F- mTabs组件,我相信任何一个前端开发工程师最多半天都能搞定,而且还能把延迟触发、动态加载等特性也给实现了。Slide组件也不难。Carousel组件看起来稍微复杂一些,但熬上一个通宵研究研究循环的实现,也不是什么大难题。1 ?; |# w* c2 K" u
上面是传统思路。新思路是:我们真的需要分三个组件来实现吗?
% F. s, o( E/ t* d" w2 Q8 T仔细思索,我们可以提取出这三个组件的共同点:# J; E# }3 W6 b" C
都有一组触点(Triggers)。 Slide的触点是数字,Tabs的触点是标签头,Carousel的触点是小图。" u+ g* o* O; D+ K; V
都有一组面板(Panels)。就是内容区域。
, P) L5 j0 y( j9 U) I通过触点可以让面板切换(Switch)。! {* e) D- r$ o4 V7 F9 g9 q1 }
上面的几条里有一个很重要的概念:切换。所有这些组件的共同点是可切换的。如果我们实现了一个可切换(Switchable)组件,上面三种组件就都是特例了。. j/ Q3 c1 g6 R
根据上面的抽象,很容易实现switchable.js:
/ c9 Y* ?; w7 a- ^function Switchable(container, config) {
( a! b4 l9 B8 c6 i5 P6 v
% G3 u. a6 `. `* U' p2 ~$ N5 S}0 z# S, P  ^4 F
S.mix(Switchable.prototype, {
8 J! F4 B: S  I  r& Y( ^init: function() { }- A% f- I5 w! a
switchTo: function() { }$ [  y/ u: ?# |9 {5 P1 I; o- u
next: function() { }2 }% _( d4 C1 L, S' Q" C& Y
prev: function() { }2 y) j: O7 A) y3 T2 s
});
" Y- o9 t1 Q3 {9 |7 x) I) j- I上面仅实现了基本的切换功能,我们可以进一步通过插件来实现更多功能:" r7 t* c; z0 y1 x( u
plugin-autoplay.js – 提供自动播放功能。1 y0 U6 O6 m# C- g+ j5 W2 ~* l
plugin-effect.js – 提供切换时的各种特效。
# l9 [/ W) z. \  i, ^& ~plugin-circular.js – 提供循环切换功能,比如Slide自动播放到第5张时,能无缝循环播放到第1张。
& x) Q5 n/ W: J; rplugin-lazyload.js – 提供数据的延迟加载功能。2 A+ p# F7 Z' \3 R4 `: M. r+ U
插件的实现在JavaScript这种动态语言里是小菜一碟。至少有两种思路,一种是埋好钩子(hooks),插件根据钩子进行扩展:
. b/ t3 d4 T+ y' l+ e( ^: X5 O' oS.mix(Switchable.prototype, {
' j. W/ D! @# k6 O% a2 xinit: function() {. I- A4 m3 u% ?/ p

" j3 M6 H% |  k4 [7 Y" gS.each(Switchable.Plugins, function(plugin) {
) k3 }3 R7 o4 O( A4 D! _% p5 z3 dif(S.isFunction(plugin.init)) {5 A! G( ^8 M* n6 D6 z
plugin.init();
4 U" }2 ~1 b0 t) [% Q" m# M}
  J  U6 u; H: x! x$ s' U});9 t. R6 n, O+ Y6 L# ]6 e$ X" y0 m/ E) `
},
8 J7 P7 L, \) e* y2 aswitchTo: function(index) {
4 D  ~* y2 L/ Q" Q, }9 U. z4 B( S1 T4 p/ ^
this.fire(‘beforeSwitch’, {toIndex: index});% p% y  }  ?2 v5 W5 U4 u; i
: r5 e% [9 ^, W4 l: P
( q/ I+ m- q3 r6 n, P0 q% t/ R

' S  q! Z9 w# e) I4 f1 U' p( ^& g}" K! p" O! D/ B, u) a
});% @3 c0 l  X- i# J0 [- E' H
在插件的代码里,定义好init方法,以及监听相关事件(事件可以看成是一类hooks)即可实现需要增加的功能。! T  [! C) `! V7 p: Z
插件的第二种实现方法是动态修改基础对象,可以重写某些方法,也可以利用AOP特性,将增加的功能织入到基础对象中:
6 M. \, W9 d5 ^  j" W- \S.weave(function() {: [# ^* Z  Z* g. Z8 Q0 Q
// plugin code
( ?3 o) b1 h2 |: O1 {});
! S+ l0 V7 `8 ?: p}, ‘after’, Switchable.prototype, ‘init’);
5 G6 a) j# l! }3 V7 W上面的代码表示在Switchable的init方法执行完成后,再紧接着执行plugin code。
; _% j( G/ B$ ^8 S通过这种方式,我们无需用到任何继承概念,没有super,没有extend,利用JavaScript的原生动态语言特性,就比较完美地解决了问题。
& V/ D: Y$ A9 N) K从Switchable的角度看,上面三个组件可以描述为:' x# A) p: ]0 c% d* Q( A) a" d
Tabs是普通的Switchable组件。
: |3 l$ F3 y# J8 `: ^8 p$ ?Slide是可自动切换且切换时有特效的Switchable组件。
- S0 t+ v* {: P  fCarousel是可自动切换、切换有特效、可循环切换的Switchable组件。
0 t; ?' z. i; }& I2 ]来看下Slide的实现,变得非常简单:
9 `& W. n$ A! \: Y1 ~var defaultConfig = {/ C+ L3 W+ d: M7 m  r
autoplay: true,* r: S; M" d3 z+ Z% v
circular: true
7 D# G1 a# h, V! `9 z5 r& z& H1 X};+ Z. F$ @2 U$ @& y; p+ l
Function Slide(container, config) {4 M+ E# j9 U3 I# X  H' Y1 D
config = S.merge(defaultConfig, config);
8 }! I5 P1 U6 G3 Y7 A' @: ASlide.superclass.constructor.call(this, container, config);) L: S/ b0 _! R) P! x! O
}
5 _. Q* N6 C' y; E9 ]4 qS.extend(Slide, S.Switchable);; H+ n2 w) m0 u2 s  j" v+ k
这里用到了类似YUI的extend方法,实现了继承,同时较好的保持了JavaScript的原汁原味。
/ H, d/ p+ R2 ?0 R" e& n( y可以看出,Tabs、Slide和Carousel组件,彼此之间没有本质差别。封装出这三个类,仅仅是为了让开发者能方便快捷的调用(这些是高级 API)。对于资深开发者来说,在实例化Switchable时,通过传入不同参数即可实现所需效果(Switchable是中级API)。  @0 T! d: i! Y6 m; c- T
更有意思的事情是,换一种思路实现代码后,也能帮助我们换一种思路看世界:
0 n' T+ P3 U% W' E0 o3 X3 P这个组件,可以看成是触点为图片的Tabs组件。左右两个翻页,无非是调用next/prev方法。进一步:, j) B  X3 ^7 I" P6 D# B
Tabs组件可以看成是仅有基本切换功能的Slide。* h) b6 I5 d+ J; O9 E
Slide可以看成是触点悬浮在图片上面的Tabs。/ P5 w( k2 _8 t/ Y9 f
等等等等5 x* v% J6 K$ [  |
最后会发现,这三个组件,本身就是相通的。原本同一物,何必分开实现呢?我们可以得到一个结论:
3 v7 Q1 b9 _  y4 [! d8 j只要符合switchable可切换特性的UI组件,原则上都可以通过Switchable实现。
唯一限制的是您的想象力!
% F' Z1 t$ D+ y; D0 s% N1 J( y比如,在Switchable的基础上,我们可以进一步实现Album(画报),实现CoverFlow(仿iTunes的封面切换效果)等等。7 }' i+ d1 d- w" E/ B4 ?1 q  W# ]7 N4 c
上面对UI组件的思维角度是可切换(Switchable),这是一个形容词。进一步思考,我们还可以得到以下形容词:
* X. j2 W3 f- E+ }Draggable – 可拖曳的* I  F- Q; R7 @) Z
Positionable – 可定位的" _6 t: A  J. I8 \1 u7 x
Selectable – 可选择的8 ]8 v5 V9 O8 c2 \
Sortable – 可排序的  Q  W1 ]# _0 M5 E
Stackable – 可堆叠的5 A* y- y, B0 I  c5 T5 y5 N& W" a
Clickable – 可点击的
8 h" `. \2 |0 lAjaxable – 可ajax的
3 y7 E$ ]& Z1 w& g$ o; H$ [% Z  C( U& S& {6 J
在这种新思路下,前端UI组件的基本组成要素已不是Panel、Memu和Button等传统UI基础单元,而是上面这些形容词。假设我们要实现一个可拖曳的可动态加载数据的可排序的表格时,或许像下面这样写一行代码就实现了:
: [. \8 G6 X! fS.Widget(‘tableId’).draggable().ajaxable().sortable();3 ^( G1 C2 y0 E
这是一个梦想。但有梦,去追,说不定就能实现
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-10 18:59 , Processed in 0.163959 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表