suixinCaesar 2011-10-31
我上大学那会儿从事的项目用的是jQuery,毕业后工作所从事的项目用的是MooTools。很幸运短期内有机会接触两款不同设计风格的优秀的JavaScript库。今天就我自己的一些认识比对下这两个JS框架,更多的是希望大家能够对MooTools这个JS框架有更多的认识。毕竟,大多数从事web前端的人对上手容易的jQuery更熟悉些。
一、API设计
就API的设计上来讲,无论是MooTools,或是其他类似YUI的JS框架都比jQuery略逊一筹。
API是什么?根据某些著作的回答,API是对所需知识的抽象,它将系统的复杂性隐藏起来。例如汽车的方向盘,电视机上的按钮。一个API设计的好坏可以从下面几个方面评估:可理解性、一致性、预见性、简单性、保护性。
◆ 可理解性
例如selector(选择器)。随便举个选择器的例子(jQuery):
$("#jQuery div")
选择器的写法是与CSS的选择器是一脉相承的。因此对于很多写页面的人来讲,可以很轻松地使用jQuery找到自己想要的那根葱。jQuery后来的版本之所以把XPath干掉,或许就是因为理解性上的问题吧。当然,MooTools也是支持CSS选择器的,只不过,其不是使用一个美元符号$,而是两个$$,貌似是学习prototype框架,返回的是数组。
$$("#MooTools div")
如果MooTools只走到这一步,我觉得还好,貌似跟jQuery的可理解性打个小平手。只可惜,MooTools还有一个单个美元符号$的选择器,其中参数只能是元素的id,例如:
$("MooTools")
这玩意,选择的貌似是有个MooTools元素扩展方法的对象。如果不存在,应用元素方法的时候(eg: addClass)则会报错。
也就是说MooTools选择元素,根据用法的不同,有时候返回的可能就是数组,有时候又是元素对象。这难免让对JS熟悉,对CSS较熟悉的人员有了理解和使用上的困难了。不过这不能怪MooTools,设计架构上的不同导致MooTools有时候需要返回元素对象,而不是元素对象数组。
因此,从理解性上来说,MooTools逊于jQuery了。
◆ 一致性
JS框架的一致性的主要体现之一就是链式调用,例如(jQuery):
$("#jQuery").css("color", "red").height(200);
MooTools也是支持链式调用的,例如(MooTools):
$$("#MooTools").setStyle("color", "red").setStyle("height", 200);
在链式调用上,jQuery和MooTools基本上都是很OK的。
但是,在其他方面的一致性上,MooTools似乎又落于下风了。jQuery这个框架真的很神奇,一个$符号就可以从广州走到北京,真可谓万能密匙啊。
首先选择器,例如$("#jQuery")肯定是$符号,然后,其相关的其他些方法,例如插件机制之$.fn.extend(object),或者是Ajax请求$.ajax([options]),又或是浏览器检测,数组对象方法等等等等都是$符号横行的。
但是,MooTools桑中$符号出现在选择器,让人难懂的$A, $E,以及一些公共方法命名上,其余时候都回家睡觉觉了。
继承需要new Class()构造,Ajax请求为new Request([options]),然后浏览器检测又是以Browser开头。数组,字符串等方法又都是当前对象本身打头,如myString.trim()。毫无一致性可言,显然,这个的学习成本和使用难度要比jQuery大多了。
◆ 预见性
好的API应该考虑到用户的无绪性选择(selective cluelessness)以及预见未来可能发生的一些事情。
不知是John Resig(jQuery 之父)确实想的很远,还是因为幸运,或是只因为John是个天才,jQuery的设计让其似乎不会与未来发生冲突。
举个老到啃不动的例子,过滤字符串的前后空格。jQuery的做法是:
$.trim(myString);
MooTools做法是:
myString.trim();
乍一看,似乎MooTools框架的做法更符合我们的理解 → 字符串对象有个trim方法,返回过滤前后空格的字符串方法。However,随着时间拖移,有些事情就开始发生变数了。
在ECMAScript 5(ES5)中,字符串是内置trim方法的。我们可以做个小小的测试,如下代码:
alert((" foo ").trim());
您可以狠狠地点击这里:ES5 内置trim方法测试
例如,在我的FireFox 6浏览器下,弹出的就是foo,如下截图:
对于一些有历史价值的浏览器,就不支持,例如IE7模式下:
同样类似的就是基于Function扩展的bind方法,MooTools中有,但是,ES5中也内置了。只是,比较幸运的是,无论是字符串trim方法,或是函数bind方法,虽然名称冲突了,但是,所实现的功能确是一样的,因此,也算不上冲突了。但是,万一哪天遇到个不幸,MooTools中的某个方法与ES5中的方法同名但不同功能,然后,只能老泪纵横了!
再举个预见性的例子,选择器选择对象以及DOM对象。jQuery对jQuery对象和原生dom对象进行了分离,例如:
$("input").bind("focus", function() { alert(this.value); // dom对象 alert($(this).val()); // jQuery包装器对象 });
但是,在MooTools中,一些方法是在原生DOM上扩展的,MooTools元素对象和原生dom是合体的,例如:
$$("input").addEvent("focus", function() { alert(this.value); // dom对象 alert(this.get("value")); // 扩展后的dom对象 });
“合体”的问题在于随着浏览器以及一些规范的发展,原生dom可能会支持其他的一些方法,这些方法有可能就和MooTools插件中DOM扩展方法冲突。好在MooTools的方法命名属于对称命名,且语义明显,这种设计可以有效避免与原生dom方法的冲突。但是,事情没有这简单……下面你可以看到MooTools中DOM元素方法的设计的一些问题(当然,不可否认,这种设计使用上相对灵活些)。
◆ 简单性
jQuery之所以让开发者恋恋不舍,原因之一就是其简单。jQuery的口号就是"write less, do more."
拿dom-style的API举例,MooTools, YUI等框架采用的是传统对称命名的方式(prop为property属性缩写):
el.setStyle(prop, val); el.getStyle(prop); el.setStyles({ propA: valA, propB: valB }); el.getStyles(propA, propB); // MooTools支持, 返回是个名-值对象
在jQuery里,一个CSS就把所有瓷器活都揽了:
el.css(prop); // 表示 getStyle el.css(prop, val); // 表示 setStyle el.css({ propA: valA, propB: valB }); // 表示 setStyles el.css(prop, func); // func 是一个返回 val 值的函数
虽然参数类似,但是jQuery只要记一个名字,而MooTools却要N个,且名字也比较长(必须完整且长,否则易出问题)。这也是为什么jQuery更容易上手的原因。而且,jQuery更近一步,还支持func回调,且参数可以map, val可以是函数,暗藏多多surprise啊。
显然,简单性这块又是jQuery好一些。
这里,我还想说点其他相关的东西。jQuery框架的短命名设计确实好,于是我就想对MooTools的元素方法进行扩展,使等同于jQuery的命名书写方式,一是省些代码,二是方便熟悉jQuery的新员工上手,拿还是上面dom-style的例子,MooTools如下扩展处理:
Element.implement({ //将mootools默认的setStyle以及setStyles方法转化为与jQuery类似的css()形式 css: function(key, value) { if ($type(key) == 'object') { for (var p in key) this.css(p, key[p]); return this; } if(!$chk(value)){ return this.getStyle(key); } this.setStyle(key, value); return this; } });
于是,MooTools也支持CSS方法,例如可以直接$("mootools").css({ border: "1px solid #ddd" });。恩,看上去不错,确实,上面的扩展在目前各个浏览器中是没有任何问题的。于是,初尝到了甜头,我们就想模拟其他jQuery包装器方法的扩展,例如width/height方法,如下(width举例):
Element.implement({ width: function (val) { if ($chk(val)) { return this.setStyle("width", val); } else { return this.getWidth(); } } });
看上去不错,不是吗?但是,人生不如意事八九,上面MooTools框架对dom进行的width方法扩展是有问题的。我们都知道,<img>元素是有width属性的。正如上面提到的,MooTools框架的元素方法是在DOM上面直接“合体”扩展的。于是乎,对于图片而言,使用上面扩展的width方法是会嗝屁的——原生属性与扩展方法冲突。
alert($("image").width()); //报错,显示$("image").width不是function
由此又进一步证实了jQuery在API设计上更能避免一些当下或未来潜在的冲突,更具远见性。
◆ 保护性
jQuery有个多库共存机制,可以有效地保护其他框架都在争夺的$符号,有效避免了一些兼容性的问题。
jQuery.noConflict(); (function($) { $(function() { // 使用 $ 作为 jQuery 别名的代码 }); })(jQuery); // 其他用 $ 作为别名的库的代码
而MooTools中似乎并无这样的机制。
总结:就API设计这点来讲,jQuery几乎从各个方面都强于MooTools,这也是jQuery大行其道的主要原因。
二、性能
◆ 选择器性能
MooTools框架官方页面提供了选择器性能测试页面:speed/validity selectors test for frameworks
点击右上角的"start tests"按钮,一段时间后就可以看到各个框架下各个选择器测试结果了。下表为我联系测试三次后的结果记录,可以看到,jQuery选择器的总体性能略比prototype低,比MooTools要强。但是,在我们实际些页面做交互的时候,其中选择器的差异基本上可以忽略不记(YUI在:nth-child选择器上的糟糕表现肯定要提防的)。
◆ dom操作性能
本来想从网上找一下MooTools和jQuery框架在dom操作这块的性能对比数据,结果发现,压根就没有,连个沾边的都没有。在这艰难困苦的时候,想起了伟大领袖毛主席的一句话:“自力更生,艰苦创业”。心中的小宇宙顿时爆发了,快刀乱麻,一番折腾后弄出了自己想要的测试页面。
您可以狠狠地点击这里:MooTools与jQuery一些dom操作性能对比
下面就是测试结果哈:
可以看到,除了setProperty/attr和setProperties/attrMulti方法MooTools框架性能是略微占优外,其余一些dom相关操作都完败jQuery。
小小总结:就性能这块。MooTools框架无论是选择器或是其他一些方法性能都逊于jQuery。
三、MooTools的优势在于?
根据上面两项的对比,MooTools框架在API方面没有jQuery优秀,一些DOM相关操作性能也没有jQuery好,那我们还有什么理由选择MooTools框架呢?MooTools框架的优势在什么地方呢?
在讲述MooTools优势之前我想讲点题外话,有位MooTools团队成员曾说如如下让人值得思考的话:
The MooTools team (of which I am a part) has never really focused on how popular the framework is. We are interested in writing it for our own use and for its own sake, but we don't really spend much energy trying to convince other people to use it. We don't consider ourselves adversaries to other frameworks - as I've heard it put on numerous recent occassions, we're at war with the browsers, not each other. In my own posts on the topic, my suggestion to people is to try a couple of options and choose the framework that suits your needs and your styles. You really can't make a bad choice (so please stop arguing about it!). jQuery, Prototype. YUI, Dojo, MooTools - we're all doing the same things just using different methods.
MooTools团队(我也是其中之一)从来没有真正关注过这个框架有 多受欢迎。我们只是对写这个框架感兴趣,为我们自己使用和它本身目的去写,但是我们真的没有花很多精力去让其他人去使用这个框架。我们从不认为其它框架是 我们的竞争对手——因为我曾经在很多场合都听说过这样的话,我们在和浏览器进行斗争,而不是相互斗争。在我的这篇文章中,我给你们的建议是:多尝试一些选择,然后选择适合你的需求和你的风格的框架。你真的不能做一个坏的选择(因此,请停止争吵!)。jQuery、Prototype、YUI、Dojo、 MooTools——我们都只是用不同的方法在做同样的事。
当初我第一次听史仲冬说MooTools库的时候,心里有嘀咕:这玩意,基本上都没听过,也没有什么人使用,应该是个稀奇古怪,糟糕的框架吧。但是,当我逐渐了解这个JS框架后,越来越喜欢之,其喜欢程度已经超过jQuery,因为它比jQuery更适合当前我所做的项目——项目大,大数据量处理以及很多复杂交互。
jQuery的因为其出色的API,使得即使不怎么懂JavaScript的人也能写出一些自以为靠谱的代码。再加上jQuery的重心放在了DOM上,这正好讨了热衷于页面效果交互的一些designer和csser的欢心。如果要构建中小型动态Web站点,用jQuery没错。快速、简单、优雅、漂亮。如果我是一个设计者,我也会使用它的。
但是,如果是有着成百上千页面的大项目,有些大数据量交互与处理的项目,多人合作,多模块化的项目,jQuery往往就显得底气不足,往往需要各类插件补充。要知道,jQuery插件成百上千,但大多都是狗屎。如果没有牛人把控,项目最后也就变成了狗屎!
MooTools虽然也能用在中小项目上,但是,其潜力的发挥要在大型、需要编写大量JavaScript的应用的网站项目上。这就是MooTools的优势。当然,我说话都是讲证据的,下面我就挑几点说说为何大型网站项目更适合MooTools而不是jQuery。
◆ 更多的对象方法扩展
虽然说,MooTools框架直接在Array, Function对象上做方法扩展有可能会造成语后来JavaScript规范出现重复或冲突的情况,但是,不可否认的是,这些对象方法的扩展对我们的开发节约了很多的功夫。
例如字符串方法的扩展。在jQuery中,字符串相关操作的方法貌似就只有一个trim。
但是,在MooTools中,框架自带字符串扩展方法就有14个之多,例如字符串内容检测的contains方法,清除整个字符串中多余的空白字符串的clean方法,转义正则表达式敏感字符的escapeRegExp方法,或是取整toInt()以及rgb颜色和16进制颜色相关转化的字符串方法等等。在大数据量交互的时候,字符串相关处理是很多的,MooTools框架自带的这些字符串扩展方法不可不谓带来N多便捷。
不仅仅是字符串,MooTools对数值,函数以及数组对象都进行了扩展。尤其数组,除了其原生的方法外,MooTools还对其扩展了近20个方法。这使得偶们在处理数组的时候基本上所向披靡了。总之,MooTools框架在各类JavaScript类型对象上做了新方法的扩展,以便我们可以更轻松更简单地应付各类复杂应用和众多数据处理。这显然与jQuery重在DOM的设计理念不同了。
◆ 数据处理利器之Hash概念
Hash,俗称哈希,是jQuery中没有而MooTools中有且很赞的一个玩意。Hash是一个重新实现的Object({}), 被专门用于数据的存取,和原来的Object({})的区别是: 它不会在进行存值,取值或迭代的时候处理对象的prototype中的内容。在MooTools中,Hash对象和Array对象(json基本构成啦)联合使用,不是我吹,再TM庞杂的数据,都能轻松应付。这让我想起了我们小秘书餐厅详细页的选菜交互,如果没有Hash这个玩意,我可真不知道该如何下手;或句话说,如果不是使用的MooTools作为框架,而是jQuery;这里的复杂的交互实现估计就是一场梦了。
◆ 面向对象的设计思想
MooTools是一款面向对象的设计思想框架。讲求继承,模块化与重用性。虽然说其new Class({})的构造有些让人不好理解,使用也颇有些既定规则,但是,实际上,这玩意要是能“挺过去”,还真的很有用的。举个关于半透明遮罩层的例子吧。我们构建一个名叫Overlay的类,然后其相关的代码就如下:
var Overlay = new Class({ Implements: [Options, Events], getOptions: function() { return { name:'', duration: 200, colour: '#000', opacity: 0.35, zIndex: 99, container: document.body, onClick: $empty }; }, initialize: function(options) { //...... return this.position(); }, position: function() { //...... }, show: function() { //...... }, hide: function(dispose) { //...... }, dispose: function() { //...... } });
于是,当我们需要半透明遮罩层的时候,直接:
var myOverlay = new Overlay();
Overlay类中的position, hide, show等方法都是外部可见的。我们可是使用类似:
myOverlay.hide()
让遮罩层隐藏。当然,我们可以再定义一个新的类,这个类继承或使用Overlay类,例如我们很常用的弹框效果咯,等等。虽然,MooTools这种强制面向对象风格的设计有些淡化了JavaScript语言本身的一些优美特性。但是,当项目庞大,JavaScript代码量巨大的时候,这种面向对象的设计理念还是相当受用的。项目越大,偶们所节省的代码量就越多。
◆ Cookie的读写
MooTools框架自带Cookie读写的方法,支持指定域和路径。而jQuery框架中要想实现Cookie的读写,需要借助于专门的cookie插件,额外的插件,显然jQuery就折腾了。HTML5 localStorage本地存储实际应用这篇文章的IE浏览器Cookie的读写就是使用MooTools框架自带的Cookie方法。很简单,很好使用。
◆ swf文件的载人
MooTools框架自带Swiff类,可以实现页面上flash文件的载入,而不需要求助google的swfobject方法。
上面这个钟表flash的效果代码如下:
var obj = new Swiff('http://www.zhangxinxu.com/study/flash/as3_clock_2.swf', { width: 300, height: 300, container: $("swfBox"), params: { wmode: 'opaque', bgcolor: '#eeeeee' } });
◆ 更全面的动画效果
MooTools框架的动画效果实际上要比jQuery的强些,但是,不足在于其使用的API要比jQuery逊色。
说MooTools框架动画要比jQuery强,其中之一就是MooTools动画内置了贝塞尔运动曲线关键字。如:'linear', 'quad:in', 'back:in', 'bounce:out', 'elastic:out','sine:in:out'等等(下图为Fx.Transitions方法之expo),而jQuery实现类似缓动效果,还需要借助ease插件。
◆ form表单序列化提交
MooTools自身携带HTML表单控件元素数据序列化方法,例如:
$('myForm').toQueryString(); //返回 "[email protected]&zipCode=90210"
表单元素自身携带了ajax提交方法,如下示例:
$('loginForm').addEvent('submit', function(e){ e.stop(); //不提交form $('loginForm').send({ onComplete: function(result) { $('loginPopup').set('html', result); //显示结果 (function(){ $('loginPopup').hide(); }.delay(1000)); //一秒钟后隐藏 } }); });
而jQuery框架要实现类似的功能,还需要专门的表单序列化插件。我靠,这东一个插件,西一个插件,而且插件质量良莠不齐,搞多了会死人的。
四、最后的对比总结
首先,毋庸置疑的是jQuery和MooTools都是非常优秀的轻量级的JavaScript框架。
jQuery以其出色的API,出色的插件机制,以及在DOM方面的关注使其大受欢迎。然而,在面对大型项目的时候,其在一些功能特性上的缺失往往需要借助插件。如果团队没有有牛人把关的话,插件的滥用,人人都插一手的代码会把项目代码质量逐渐变成狗屎的。一般而言,jQuery更适合与动态的中小站点。
MooTools插件虽然在API & DOM和上手容易程度上都上不及jQuery,但是,其API以及面向对象的设计思想似乎在实际的大项目中更有价值。
因此,如果我要折腾我的个人网站,应该会使用jQuery。但是,如果是公司的大的网站项目,我会坚定不移选择MooTools,因为更适合。