Kissy有比较全面的 官方文档 ,还有一个叫做 15天学会Kissy 的教程。
Kissy比jQuery功能强大的一个副作用就是——你得掌握比使用jQuery时更多的概念。回想一下jQuery中都有哪些概念?反正我只对jQuery对象和链式操作有印象。
以Kissy的层次结构为线索展开讨论吧。Kissy按照功能的使用频率将它提供的所有特性分成了大致上的三层:种子(seed)、核心(core)和组件(components)。
kissy的种子可以看做是核心中的核心。提供了对象的继承机制、对JavaScript语言上的增强、异步模块加载机制、实现了promise规范并且还有几个简单的客户端辅助函数。
kissy的对象继承机制很有意思,采用的是原型继承与对象增强的混合方式。Kissy在对象的混合方面提供了好几个不同作用的方法。
值得一说的就是这个异步模块加载机制,有迹象表明这是从YUI借鉴来的,源码稍微读了一点点,应该是kissy中比较有意思的点,有机会要仔细研究下。
另外一个比较陌生的概念是promise规范。自行google到的资料表明,这个规范是为了解决异步调用的嵌套问题而出现的。试想一下,当有一系列异步事件,每一个都是在上一个事件结束之后调用,那么很容易就会写出很深的嵌套代码来。当然,嵌套代码也不是什么大不了的,但对于有代码洁癖的人来说是不可容忍的,于是出现了这么个promise规范。prominse从字面意思上来看是“承诺”,大致就是当开始一个异步调用的时候,需要给出一个承诺,当成功了如何如何,失败了又如何如何。调用then()方法可以定义下一个异步事件,总之,就是把嵌套调用做成了链式调用。
Kissy的种子加上核心,大致实现了jQuery的所有功能。当然,只是大致,两个框架不可能做到完全重叠,请不必过于较真。
Kissy主要实现了事件机制、DOM节点操纵和ajax,当然还有一些其他常用的辅助功能比如cookie和浏览器判断。
从这一层起,Kissy在设计上就出现了模块的概念,例如事件和ajax,这些特性都是组织在不同的模块中的。种子这一层没有模块的概念,因为是种子这一层提供了模块加载机制,如果硬要较真的话,从模块构建工具的角度来看seed本身算作一个模块。文档中seed下的lang、loader等是seed的子模块,而在core部分的文档中,event、dom等可都是实实在在的模块,也就是说seed中的lang和core中的dom地位是不对等的,这一点在文档中并没有体现出来,或许是文档作者不希望再引入额外的复杂度了吧。
貌似Kissy努力要讨好YUI和jQuery的用户,同时提供了两种使用风格。单纯在使用上来说,确实方便了,但是在了解Kissy的结构上却带来了额外的麻烦。例如dom模块和node模块,从名字上说感觉怪怪的,因为平时两个词一般都是同时用的,称为“DOM节点”。实际上node模块是一个迎合jQuery风格的存在,node模块包含了dom、event和anim三个模块的所有功能。
在node模块,使用选择器方法得到的是NodeList对象,大致相当于jQuery对象;而使用DOM模块的选择器方法得到的是原生的DOM节点。另外,在NodeList对象上可以调用的方法都是在DOM模块的同名函数,在node模块的文档中,这部分是一笔带过,并没有详细描述,而dom模块中,每一个方法基本都用详细介绍。实际上,NodeList对象的方法和dom模块中那些同名方法用法是不一样的:NodeList是一个类列表对象,类似jQuery对象它包装了通过选择器方法查询到的DOM节点,因此它可以提供一些“方法”来操作它包装的这些DOM节点,从形式上来看,它使用了一种更“面向对象”的形式;dom模块中提供的方法——或者更严格一点应该称为“函数”,因为选择器方法返回的都是原生的DOM节点或节点数组,没办法用链式操作,所以dom模块中的方法第一个参数都是一个选择符字符串,这种操作无疑是“面向过程”的风格。
另一个问题,Kissy为了模仿jQuery将“jQuery”和它的别名“$”用作唯一的命名空间这种设计,将node模块提供的方法都扩充进了KISSY这个根命名空间,所以KISSY下提供的方法,可能来自与seed,也可能来自于别的模块。当然,这对使用者来说不成问题,但问题在于似乎node模块是唯一使用jQuery风格的模块,其余仍是YUI风格,想使用Kissy全部特性的话仍是不可避免要使用YUI风格,恐怕要在jQuery和YUI两种风格间自由切换,还真是需要一段时间才能适应。值得一提的还有一点,dom模块将get()和query()方法也扩充进了KISSY根命名空间,这两个方法和node模块中的one()和all()对应,只是要注意它们的返回值类型。
Kissy根据不同的模块所要解决的问题,灵活采用了面向过程和面向对象两种不同的程序设计风格。在一些简单的模块,如cookie和dom,采用的就是面向过程的风格,使用一个Object类型的对象作为模块的命名空间。也有模块是以面向对象的风格设计的,通常这种情况下作为模块命名空间的是一个函数对象,函数对象在JS中可以作为“类”的概念而存在,那么从面向对象的角度看,以这个函数对象作为命名空间下的函数都是些静态函数。
以函数对象作为命名空间的好处是它可以被调用,通常调用的返回值就是一个这种类型的对象。jQuery基本上就是这种设计思想:调用jQuery()(或它的别名“$”)方法返回值是一个jQuery类型的对象。看上去这很像是一个构造方法,但严格说来因为没有使用new关键字调用,实际上只相当于一个工厂方法罢了。anim模块使用的就是这种设计,调用Anim()方法返回一个Anim对象,对象有一系列方法可供调用,同时Anim也提供了相应的同名静态方法——换个角度也就是Anim模块命名空间下的函数。
Kissy中有些对象暴露出来供用户使用,也有很多对象只在底层使用,他们作为幕后英雄在基层默默地工作,用户根本不需要知道它们。然而在ajax模块我却发现了一个很奇葩的现象:作为模块命名空间的Ajax就不必说了,但是文档上经常使用的却是IO,还有小写的io……这可把人搞糊涂了。稍微研究了一下,发现原来Ajax、ajax、IO和io这四个名字其实都是同一个东西,并且文档上没有任何特别说明——你们这是要闹哪样嘛。或许保留这么多别名是为了兼容旧版本,但是至少在文档中提一下,然后给个建议用法就好了嘛,不过现在看来连文档也没统一用法。
KISSY作为唯一的根命名空间,是一个Object类型的对象,一般为了简化编程通常都会定义一个局部变量S作为它的别名来使用。一直以来都很讨厌使用大写字母尤其是全部大写,后来发现C语言中很多库都习惯拿库名的大写形式做库函数的前缀,JS框架也是从JS库为雏形发展起来的,继承了这样的命名方式也不足为奇了。KISSY也照搬了YUI这样的命名方式全部搞成了大写。
Kissy下的各个模块也都有各自的命名空间,这个之前已经说过了,但是刚接触Kissy最迷惑的问题也在于此。按理说“包”、“模块”和“命名空间”应该指的是同一种概念,但是在Kissy中,模块和命名空间指的是两种概念。模块和目录结构有关,并且在Kissy的命名规范上也看得出来——它们应该是小写的,多个单词用短划线“-”分隔。作为命名空间的对象命名则是首字母大写,有的缩写词则全部大写(比如DOM、IO,但奇怪的是同样为缩写词的Ajax则只是首字母小写)。
Kissy的seed和core是默认加载的,也就是说可以直接使用YUI的风格进行调用,比如: KISSY.DOM.get(...)
。另外也可以使用Kissy异步模块加载的方式来使用:
KISSY.use("dom", function(S, DOM) {
DOM.get(...);
});
Kissy的use方法接受两个参数,第一个参数是字符串类型的模块列表,可以一次加载多个模块,模块间用逗号隔开。第二个参数是模块加载完毕后的回调函数,参数列表中第一个参数永远是KISSY本身,习惯用法是使用S作形参。接下来就是加载的模块列表,按习惯,形参的名字也是首字母大写的形式。
至于真正需要异步加载的模块,看样子只能使用后一种方法了。当然形参可以任意定,不过仍然建议使用官方推荐的写法。