JavaScript框架设计
上QQ阅读APP看书,第一时间看更新

前 言

首先说明一下,本书虽是讲解框架设计,但写个框架不是很深奥的事情,重点是建立更为完整的前端知识树。只有自己尝试写个框架,才有机会接触像原型、作用域、事件代理、缓存系统、定时器等深层知识,也才有机会了解applyElement、swapNode、importNode、removeNode、replaceNode、insertAdjacentHTML、createContextualFragment、runtimeStyle 等偏门 API,也才会知晓像getElementById、getElementsByTagName、setAttribute、innerHTML 存在大量的 Bug,当然你也可以尝试最近几年浏览器带来的新API(包括ECMA262v5、v6、HTML5或大量误认为是HTML5的新模块),如Object.defineProperty、CSS.supports、WebKitShadowRoot、getDefaultComputedStyle……

虽然这难免落入“造轮子”的怪圈中,但“造轮子”在这世界却是出奇普遍。一般创造性的活动,一开始都是临摹他人的作品。就算不“造轮子”,也要收集一大堆“轮子”,作家有他的素材集,设计师有大量icon与笔刷,普通的“码农”也有个commonjs存放着一些常用的函数。以前的程序员们,经常会为了做一个数据处理程序而自己开发一门编程语言。如Charls Moore,他在美国国家天文台做射电望远镜数据提取程序时开发了 Forth;高德纳为了让自己写的书排版漂亮些,写了TeX;DHH为了做网站写了Rails……如果连写一个控件都要百度或Google查找答案,那水平不容易提高。

当前很少有技术书教你写框架的,即便是众多的 Java 类图书,大多数也是教你如何深入了解SHH的运作机理。

如果你是这两三年才接触JavaScript,那恭喜你了。现在JavaScript的应用洪荒时代已经过去, Portotype.js的幕府“统治”也已结果,且已迎来非常强势的jQuery纪元,有大量现成的插件可用,许多公司都用jQuery,意味着我们的技术有了用武之地。

但事实上还是要通过调试程序获得经验,只从 JavaScript 书上学习的那些知识点没法明白jQuery的源代码。

许多大公司的架构师根据技术发展的情况,他们都有自己一套或几套JavaScript底层库,各个部门视情况还发展针对于自己业务的UI库。而企业开发中,UI库就像流水线那样重要。而底层库只是一个好用的“锤子”或“胶钳”。要想迅速上手这么多公司框架,基础知识无疑是非常重要的。假若之前自己写过框架,那就有了经验。道理是一样的,框架设计的一些“套路”肯定存在的。本书就是把这些“潜规则”公开出来,迅速让自己成长为技术达人。

1.框架与库

下面稍微说一下框架与库的区别。

库是解决某个问题而拼凑出来的一大堆函数与类的集合。例如,盖一个房子,需要有测量的方法、砌砖的方法、安装门窗的方法等。每个方法之间都没什么关联。至于怎么盖房子都由自己决定。

框架则是一个半成品的应用,直接给出一个骨架,还例如盖房子,照着图纸砌砖、铺地板与涂漆就行了。在后端Java的三大框架中,程序员基本上就是与XML打交道,用配置就可以处理80%的编程问题。

从上面描述来看,框架带来的便利性无疑比库好许多。但前端JavaScript由于存在一个远程加载的问题,对JavaScript文件的体积限制很大,因此,框架在几年前都不怎么吃香。现在网速快多了,设计师在网页制造的地位(UED)也不比昔日,因此,集成程度更高的MVC、MVVM框架也相继面世。

不过,无论是框架还是库,只要在浏览器中运行,就要与DOM打交道。如果不用jQuery,就要发明一个半成品jQuery或一个半成品Prototype。对想提升自己能力的人来说,答案其实很明显,写框架还能提升自己的架构能力。

2.JavaScript发展历程

第1时期,洪荒时代。从1995年到2005年,就是从JavaScript发明到Ajax概念http://www.adaptivepath.com/ideas/ajax-new-approach-web-applications的提出。其间打了第一场浏览器战争,IE VS Netscape。这两者的DOM API出入很大,前端开发人员被迫改进技术,为了不想兼容某一个浏览器,发明UA(navigator.userAgent)嗅探技术。

这个时期的杰出代表是Bindowshttp://www.bindows.net/,2003年发布,它提供了一个完整的Windows桌面系统,支持能在EXT看到的各种控件,如菜单、树、表格、滑动条、切换卡、弹出层、测量仪表(使用VML实现,现在又支持SVG)。现在版本号是4.x,如下图所示。

其他比较著名的还有Dojo(2004年)、Sarissa(2003年)、JavaScript Remote Scripting(2000年)。

Dojo 有 IBM 做后台,有庞大的开发团队在做,质量有保证,被广泛整合到各大 Java 框架内(struct2、Tapestry、Eclipse ATF、MyFaces)。特点是功能无所不包,主要分为Core、Dijit、DojoX 3大块。Core提供Ajax、events、packaging、CSS-based querying、animations、JSON等相关操作API。Dijit 是一个可更换皮肤、基于模板的 Web UI 控件库。DojoX 包括一些新颖的代码和控件,如DateGrid、charts、离线应用和跨浏览器矢量绘图等,如下图所示。

JavaScript Remote Scripting是较经典的远程脚本访问组件,支持将客户端数据通过服务器做代理进行远程的数据/操作交互。

Sarissa封装了在浏览器端独立调用XML的功能。

第2时期,Prototype“王朝”,2005年~2008年。其间打了第2次浏览器战争,交战双方是 IE6、IE7、IE8 VS Firefox 1、Firefox 2、Firefox 3,最后Firefox 3大胜。浏览器战争中,Prototype积极进行升级,加入诸多 DOM 功能,因此,Jser(JavaScript 程序员)比之前好过多了。加之有 rails、script.aculo.us(一流的特效库)、Rico等助阵,迅速占领了市场。

Prototype时期,面向对象技术发展到极致,许多组件成套推出。DOM特征发掘也有序进行,再也不依靠浏览器嗅探去刻意屏蔽某一个浏览器了。无侵入式 JavaScript 开发得到推崇,所有JavaScript代码都抽离到JavaScript文件,不在标签内“兴风作浪”了。

Prototype的发展无可限量,直到1.5版本对DOM进行处理,这是一个错误详见Prototype核心成员的反思:http://perfectionkills.com/whats-wrong-with-extending-the-dom/。如它一个很好用的API-getElementsByClassName,由于W3C的标准化,Prototype升级慢了,它对DOM的扩展成为了它的“地雷”。

第3时期,jQuery纪元,2008年到现在(如下图所示)。

jQuery则以包裹方式来处理DOM,而且配合它的选择器引擎,若一下子选到N个元素,那么就处理 N 个元素,是集化操作,与主流的方式完全不一样。此外,它的方法名都起得很特别,人们一时很难接受。

2007年7月1日,jQuery发布了1.1.3版本,它的宣传是。

(1)速度改良:DOM的遍历比1.1.2版本快了大概800%。

(2)重写了事件系统:对键盘事件用更优雅的方法进行了处理。

(3)重写了effects系统:提高了处理速度。

停滞不前的Prototype已经跟不上时代的节奏,jQuery在1.3x版本时更换Sizzle,更纯净的CSS选择器引擎,易用性与性能大大提高,被程序员一致看好的mouseenter、mouseleave及事件代理也被整合进去,jQuery就占据了市场。

3.JavaScript框架分类

如果是从内部架构与理念划分,目前JavaScript框架可以划分为5类。

第1种出现的是以命名空间为导向的类库或框架,如创建一个数组用new Array(),生成一个对象用new Object(),完全的Java风格,因此我们就可以以某一对象为根,不断为它添加对象属性或二级对象属性来组织代码,金字塔般地垒叠起来。代表作如早期的YUI与EXT。

第2种出现的是以类工厂为导向的框架,如著名的Prototype,还有mootools、Base2、Ten。它们基本上除了最基本的命名空间,其他模块都是一个由类工厂衍生出来的类对象。尤其是mootoos 1.3把所有类型都封装成Type类型。

第3种就是以jQuery为代表的以选择器为导向的框架,整个框架或库主体是一个特殊类数组对象,方便集化操作——因为选择器通常是一下子选择到N个元素节点,于是便一并处理了。JQuery包含了几样了不起的东西:“无new实例化”技术,$(expr)就是返回一个实例,不需要显式地new出来;get first set all 访问规则;数据缓存系统。这样就可以复制节点的事件了。此外,IIFE (Immediately-Invoked Function Expression)也被发掘出来。

第4种就是以加载器串联起来的框架,它们都有复数个JavaScript文件,每个JavaScript文件都以固定规则编写。其中最著名的莫过于AMD。模块化是JavaScript走向工业化的标志。《Unix 编程艺术》列举的众多“金科玉律”的第一条就是模块,里面有言——“要编写复杂软件又不至于一败涂地的唯一方法,就是用定义清晰的接口把若干简单模块组合起来,如此一来,多数问题只会出现在局部,那么还有希望对局部进行改进或优化,而不至于牵动全身”。许多企业内部框架都基本采取这种架构,如Dojo、YUI、kissy、qwrap和mass等。

第 5 种就是具有明确分层构架的 MV*框架。首先是 JavaScript MVC(现在叫 CanJS)、backbonejs和spinejs,然后更符合前端实际的MVVM框架,如knockout、ember、angular、avalon、winjs。在MVVM框架中,原有DOM操作被声明式绑定取代了,由框架自行处理,用户只需专注于业务代码。

4.JavaScript框架的主要功能

下面先看看主流框架有什么功能。这里面包含jQuery这个自称为库的东西,但它接近9000行,功能比Prototype还齐备。这些框架类库的模块划分主要依据它们在github中的源代码,基本上都是一个模块一个JavaScript文件。

jQuery

jQuery强在它专注于DOM操作的思路一开始就是对的,以后就是不断在兼容性、性能上进行改进。

jQuery 经过多年的发展,拥有庞大的插件与完善的 Bug 提交渠道,因此,可以通过社区的力量不断完善自身。

Prototype.js

早期的王者,它分为4大部分。

· 语言扩展。

· DOM扩展。

· Ajax部分。

· 废弃部分(新版本使用其他方法实现原有功能)。

Prototype.js的语言扩展覆盖面非常广,包括所有基本数据类型及从语言借鉴过来的“类”。其中Enumerable 只是一个普通的方法包,ObjectRange、PeriodicalExecuter、Templat则是用Class类工厂生产出来的。Class类工厂来自社区贡献。

mootools

它由于 API 设计得非常优雅,其官方网站上有许多优质插件,因此才没有在原型扩展的反对浪潮中没落。

RightJS

又一个在原型上进行扩展的框架。

MochiKit

一个Python风格的框架,以前能进世界前十名的。

Ten

日本著名博客社区Hatena的JavaScript框架,由amachang开发,受Prototype.js影响,是最早以命名空间为导向的框架的典范。

mass Framework

它是一个模块化,以大模块开发为目标,jQuery式的框架。

经过细节比较,我们很容易得出以下框架特征的结论。

· 对基本数据类型的操作是基础,如jQuery就提供了trim、camelCase、each、map等方法, Prototype.js等侵入式框架则在原型上添加camelize等方法。

· 类型的判定必不可少,常见形式是isXXX系列。

· 选择器、domReady、Ajax是现代框架的标配。

· DOM操作是重中之重,节点的遍历、样式操作、属性操作也属于它的范畴,是否细分就看框架的规模了。

· brower sniff已过时,feature detect正被应用。不过特性侦测还是有局限性,如果针对于某个浏览器版本的渲染 Bug、安全策略或某些 Bug 的修正,还是要用到浏览器嗅探。但它应该独立成一个模块或插件,移出框架的核心。

· 现在主流的事件系统都支持事件代理。

· 数据的缓存与处理,目前浏览器也提供data-*属性进行这面的工作,但不太好用,需要框架的进一步封装。

· 动画引擎,除非你的框架像Prototype.js那样拥有像script.aculo.us这样顶级的动画框架做后盾,最好也加上。

· 插件的易开发和扩展性。

· 提供诸如Deferred这样处理异步的解决方案。

· 即使不专门提供一个类工厂,也应该存在一个名为extend或mixin的方法对对象进行扩展。jQuery虽然没有类工厂,但在jQuery UI中也不得不增加一个,可见其重要性。

· 自从jQuery出来一个名为noConflict的方法,新兴的框架都带此方法,以求狭缝中生存。

· 许多框架非常重视Cookie操作。

最后感谢一下业内一些朋友的帮忙,要不是他们,书不会这么顺利地写出来。以下排名不分先后:玉伯、汤姆大叔、弹窗教主、貘大、linxz 、正则帝 abcd。这些都是专家级人物,在业界早已闻名遐迩。由于本人水平有限,书中难免存有不妥之处,请读者批评指正,源程序和答疑网址:https://github.com/RubyLouvre/jsbook/issues。编辑联系邮箱:zhangtao@ptpress.com.cn。