2010年1月29日 的存档

  • YUI3设计中的激进和妥协

    日期:2010.01.29 | 分类:前端技术 | 评论数:0

    相信每个前端工程师都有自己喜爱的 javascript框架,说情感也好,道信仰也罢,javascript框架带给人的不仅仅是便捷的开发,更有一种纯粹的逻辑美感,不管是jquery 曼妙的简洁,还是yui魔术般的沙箱,都使我们产生无穷的想象。然而,js框架却又必然无法做到面面俱到的完美无瑕,比如jquery在OO方面做出的让步,以及yui在性能上做的牺牲,无不给人传达一种缺憾美、一种理想的现实主义。今天,我们来看看yui3在框架设计中的这些牺牲和让步,以便让我们更加深刻的理解yui3的全貌,并将其优势发挥至最佳。

    1,种子的一步到位 or 颗粒化
    所谓种子一步到位是指只要在页面引入一个种子文件的script标签,比如prototype和jquery,只要引入一个prototype.js或 jquery.js就可以了,他们将各自对dom操作和event的封装等等都囊括进一个文件中,并尽力将其做到最小,这样做的好处是显而易见的,使用框架非常简单。然而yui将这些功能做了级别划分和颗粒化设计,从概念上抽象出来“核心”、“工具”和“组件”,每个小功能放在一个文件当中,需要的时候则要自行去引用,yui文档中给出的大量demo都采用这种方法,这种设计显然不像jquery那样对初学者友好,而且使用起来不够傻瓜,为了实现一个小功能,甚至要引入三四个js文件。yui这样做的原因有两个,一是yui实在太大,把所有功能都搞进一个文件中确实有点不靠谱,二是为其动态加载的框架设计做铺垫。

    2,手动引入 or 动态加载
    往页面中写js的传统方法是,直接将js文件作为script标签路径写在页面中,使用yui也可以这样引入页面,但yui更推荐使用loader进行动态加载。动态加载脚本的渊源很复杂,目前来看主要原因有三,其一,页面中手写js标签无论如何都会占用一个http请求,即使这个请求是一个304,动态加载的文件缓存后则不必发起真实的http请求,其二,动态加载可以实现按需加载,而且可以根据js文件之间的依赖进行去重和排序,手写标签加载js文件则必须让开发者去额外关注一下文件的排序、重复等等,其三,动态加载有利于页面代码的语义化,这使得开发者只关心“需要什么”,而不用去在意“如何得到”。当项目变得越发臃肿,维护成本越来越高的时候,这中小技巧会有不小的好处的。

    3,逻辑启动的单一入口 or 沙箱
    我们在页面中启动一个js逻辑通常是放在一个类似onDomReady的方法中,如果页面中存在多个逻辑的时候怎么办呢?比如,a实现了逻辑A,页面代码是这样的

    
    $.onDomReady(function(){
    ___LogicA.start();
    });
    

    这段代码通常写在页面的尾部,这段逻辑所伴随的html代码是埋藏在页面的某处,这时b要在页面中增加逻辑B,b的做法是首先找到尾部的这段代码,然后更改成如下模样:

    $.onDomReady(function(){
    ___LogicA.start();
    ___LogicB.start();
    });
    

    同样,B逻辑所伴随的html代码也是埋藏在页面的某处,这样看来,js逻辑和其伴随的html代码如此分离,以至于到了删减功能的时候,往往删掉 html却忘了删js,或者删掉js忘了删除html,在重用代码的时候也会是个麻烦。同样,在调试的时候,工程师也要打开两个窗口分别关注js和 html,即使这两段代码在同一个文件当中。如此则不如把代码写成这样:

    A逻辑的html代码段
    $.onDomReady(function(){
    ___LogicA.start();
    });
    
    …
    
    B逻辑的html代码段
    
    $.onDomReady(function(){
    ___LogicB.start();
    });
    

    这种coding写法正是yui所提倡的,也就是所谓的沙箱,每个逻辑包含在一个沙箱中,各司其则互不干扰。当第三者浏览代码的时候也立即明白这就是一个独立的功能块,包含典型的html结构和启动逻辑的js,要重用,整块拷走即可。

    4,代码污染 or 沙箱
    刚才提到的沙箱可以解决一部分代码污染的问题,新人阅读代码不用再看着乱码般的源码,“瞻前顾后”小心翼翼的寻找html和js的对应关系。但这种写法也存在隐患,现在的前端开发越来越复杂也更多的使用分层,比如底层使用yui,中间层是自定义的工具库,或者再加一个项目的widget组件库,写页面逻辑则是基于这些库进行开发。这样的话,每段逻辑可能写成这个样子:

    
    A逻辑的html代码段
    $.onDomReady(function(){
    ___LogicA.start();
    });
    

    尽管我们可以约定,将项目用到的所有的组件都统一加载进页面中,但当组件越来越多的时候,就出现了上文所说的一步到位和颗粒化之间的矛盾,当每个控件单独占用一个js文件和与之相伴随的css皮肤,一个相对复杂的逻辑就变成了上文所说的手动引入js文件,并随之引入一些显而易见的弊端:

    复杂的A逻辑的html代码段,使用了日历,弹层,幻灯
    $.onDomReady(function(){
    ___LogicA.start();
    });
    

    首先,手写大量的js文件会各自占用单独的http请求,在者,这个场景中,slider.js继承自tabview.js,因此要着重关注他们的顺序,第三,如果别人在页面的其他地方也使用了日历或者幻灯,还要再加两个相同的js标签?其实,说到这里,我们已经可以隐约看到大项目团队开发的影子了,在一个迭代及其频繁的项目中,开发者需要在短的时间内完成一个复杂页面的某个功能的开发,而且不能影响到页面的其他功能,毕竟,每添加一个功能,QA mm们都要将与之牵连的所有功能都要回归,可辛苦了我们的QA mm们。在这种情况下,一个功能的开发可能和同一个页面其他功能的开发相互并行。互相不属于同一个项目组,也不知晓对方的实现。这种模式则是我们经常遇到的多人开发,冲突也大都由此产生,每个功能单独看来是正确的,合并到一起会产生冲突和bug,这时调试bug则需要两个工程师同时进行,很麻烦。甚者,当组件升级了,比如,tabview.js再继承自tab.js,则又要去联系各个工程师,将每个引用tabview.js的地方之前再加上一个 tab.js,很麻烦。我们说,这种多人协作模式所带来的冲突也是代码污染的一种,因为每个人只能掌控类似tms区块那么巴掌大的地方,所组成的最终页面是什么样,都不知道。更何况,这种生猛的引用js,也会阻塞页面加载,占用http请求,影响性能。

    鉴于此,yui将js的动态引入机制也并入其沙箱设计之中,我要引用的只是一个名字,比如slider.js,他依赖 tabview.js,tabview.js依赖tab.js,这样我只需引用一个名叫”slider”的东西即可,不用操心他依赖什么,更不用管如何引入到页面中,yui都帮我们搞定:

    TB.addmoudle({
    ___logicA:{
    ______fullpath:’logica.js’,
    ______requires:['slider']
    ___}
    }).use(‘logicA’,function(Y){
    ___LogicA.start();
    });
    

    当我们看最终组成的页面的时候,看到的只是埋藏在页面各个角落的这些简短的结构一致的js代码段。很易理解,这点代码也不用像长的代码块压成一行。(更多细节可参照 前端弱架构导致的代码污染

    5,颗粒化 or http请求数
    这的确是一对矛盾,颗粒化带来了项目开发、管理、和代码重用的高效率,却又引入了更多的http请求数,好在yui提供了combo,可以将所有的 http请求合并成一个。只需在YUI引入的时候配置下combo属性即可,高颗粒化的请求数瞬间降低一倍以上。在之前做雅虎关系的时候,在yui2和 yui3pre并存的情况下,可以将请求降低到4个,yui2和3各一个种子,各自一个combo。当然这是在hack掉yui的loader的前提下。 yui默认不会合并非yui文件(更多细节可以阅读基于yui的团队开发)。即使这样,我们仍然可以控制我们的http请求数,在不hack yui的情况下,可以解决部分性能问题。

    6,懒惰加载 or 即时加载即时执行
    上文提到,逻辑依赖沙箱,沙箱依赖的js文件则是延时加载的,这样就导致一个问题,当页面比较庞大的时候,会等待页面js加载完毕才会渲染动作,这样的用户体验不佳,而即时加载即使运行则可以渲染出模块后随即渲染动作,当网速一定的时候,两者看似是一对不可调和的矛盾,yui 动态加载的机制比较折中的处理了这个问题,A逻辑需要a.js,B逻辑需要b.js,A逻辑则只会在a.js加载完成后执行,而不管b.js是否加载完成,而当A需要a.js和b.js的时候,A则需要等待a.js和b.js都加载完成才会执行,B逻辑只需判断当前是否已经加载b.js,如果b.js已经在其他模块中引入进来,B则可以立即执行。但确定的是,所有的js的引入一定是在domReady后执行的,也就是说,不管怎样,动作的渲染一定是在页面html结构出来后才开始执行的,用户体验上还是会有损失。

    7,面向接口的设计 or 面向dom的设计
    我们知道jquery的插件习惯将所有的动作都加载到一个$(‘#id’)上,使用的时候只要执行类似$(‘#id’).init()即可。这看起来简洁明快,开发者的逻辑的思路也始终没有离开“节点”,很方便理解,而对yui3 的node扩展就不那么方便,从yui3的pre版到正式版,对node扩展的方法在不断的修改(更多细节看这里:扩展yui3 node的定时器),这也可以看出yui设计者在对node扩展性设计时的纠结和苦恼:要不要允许开发者去扩展node节点呢?大概是因为设计者们对prototype先天的弊端心有余悸。目前看来,设计者还没有完全走出纠结,尽管对node扩展相比yui3第一个预览版方便了很多,开发者仍然不能像写jquery插件那样优雅自如的对node进行扩展。相反,zakas却将更多的精力放在了widget接口的设计上,在这一点上,相比jquery,yui3则具有无可限量的优越性,因为在yui3中,组件不仅仅是组件,而是一个有血有肉的生命体,他可以出生,可以成长,可以被改造,可以死亡,组件在这些复杂的运行时环境中自我锤炼,因此,一个复杂页面在yui3的技术体系中,变成了一个由无数组件链接而成的生态系统,这种生物链所带来的设计创新和新视野是其他js框架无论如何也无法超越的。关于yui3的组件开发更多细节可以参照:基于yui3的组件开发1克军在D2上的分享《从yui2到yui3看前端的演变》

    8,苗条的身材 or 庞大的身躯
    说到这里,大概会有很多人拍砖。其实jquery和yui同属两类不同的js框架,一个苗条纤细,一个身重如山,两者之间其实没有什么水深火热,只是使用范围不同罢了,类似jquery的轻框架的使用范围是博客级别的小网站,尤其适合单人开发,代码写一次不再修改,而且很适合初学者学习使用,给初学者带来自信。 yui则使用与企业级的项目中的团队开发,项目维护周期远远超过开发周期,因此yui性能一定比不过jquery,jquery的续航能力也一定不如 yui,没有最优秀,只有最适合。当然这自然也挡不住我个人对迷人的jquery的喜爱,只要我们能从各个js框架学到东西,能提高自己做前端架构的能力,就ok。

    说了这么多,其实只有一个观点,人无完人,框架无完框架,缺憾之处必有权衡。以上YY,欢迎拍砖!

    感谢:ued.taobao.com 分享!

最近文章

最近评论

分类目录

紫枫印迹

2010年一月
« 十二   二 »
 123
45678910
11121314151617
18192021222324
25262728293031

标签云

CSS+HTML css2.0 HTML5 SEO seo优化 SNS User Friendly2009 VIP web 中文手册 互联网 交互 交互设计 产品 产品设计 产品设计师 体验 信息架构 关键词 制作网页 前端开发 原型 命名规则 垂直居中 小技巧 手机网站开发 按钮 支付宝 李开复 流程 用户习惯 用户体验 用户界面 用户研究 用户访谈 瞬间的设计 网站运营 网页设计 背景 行动 视觉设计 设计 设计师 身体语言 页面重构

用户体验设计(UED)

紫枫友情收藏

86ue_rss

设置