现在公司开发的项目大量用到了JS,由于项目模块要求不同,编程人员的开发水平也不同,前端的JS写的非常的乱,最近一段时间也在思索着如何才能以最小的成本,在不大幅提高编程难度的前提下最大化的规范前端编程.前几天看了一篇文章:使用SeaJS实现模块化JavaScript开发,然后又仔细研究了关天,感觉很不错,现将研究心得分享如下.
一.模块化
JS的模块化编程思路其实来源于一系列编程规范(如CommonJS)和此规范的一些实现(如Nodejs),虽然我对以上提到的这些东东没有什以研究,但是以我在改别人前端代码的痛苦经历和就"模块化"这三个字的字面意思来讲,还是能够理解它想表达的意思.所谓"模块化",就是以分治法为依据,把一个大的问题分解成若干个高内聚,低耦合的小的问题.最后通过一系列依赖关系明确的相互调用,最后共同解决那个大问题.这里的两个关键是高内聚与明确的低的耦合.seajs解决的正是后面一个关键.
二.seajs概述
大部分情况下,引入一个新的内容都会增加旧有内容的复杂性.既然它的目标是降低复杂性,那么它本身就应该保持足够的简单.seajs非常好的遵守了KISS原则.SeaJS仅向全局公开了两个标识符:seajs与define.
seajs对象有两个方法use和config.下面是define方法的结构图
可以看到,define的第一个参数与module的属性都有一个叫id的.其实他们是一回事,如果传入id,module则用它,如果不传入,则使用一个默认的规则生成每个模块的id.
define的第二个参数dependencies与module的属性dependencies也是有关系的.module的属性dependencies是传入的dependencies集合并上使用require方法获取的依赖的集合.
define是个重要的方法,它有三个参数:require,exports与module.这个方法的构造完全附合CommonJS下面的Modules/Wrappings规范:使用require获取“依赖”,使用exports导出“接口”,而module则代表被定义的模块本身.
seajs对象与define方法的具体使用方法可以参考官方的文档进行学习.从一般使用顺序来讲,首先通过seajs.config进行全局配置,然后通过define定义各个模块,最后通过seajs.use方法使用各模块.如果能按照这个方法去理解,应该就能够很快的掌握这个框架.
下面来谈谈我对使用seajs框架的理解
一.代码组织
如果一个网站的JS全部是由seajs组织的,你就会发现所有的js只会出现在两个地方:define里与seajs.use里.在define里,因为这里是模块定义的位置;在seajs.use里,因为这里是唯一能够使用由define定义的模块的地方.通过define定义模块,所有的功能都被封装成一个个js对象,且这些对象是闭包的,只有通过seajs.use方法的第二个参数----一个回调函数的参数才能使用这些定义的对象.
二.顺序加载
由于seajs仅仅是一个模块加载器而不是文件加载器,它不能保证脚本一定是按代码的书写顺序进行加载.为了应付这种情况,seajs推荐使用LABjs配合seajs进行使用.
Labjs的使用也比较简单,使用script方法加载脚本,使用wait方法执行脚本.更加具体的说明请参照其官方文档.
三.循环引用
参见下面三段代码:
//a.js define(function(require, exports, module) { var b = require("b"); alert(b); return b + 1; })
//b.js define(function(require, exports, module) { var a = require("a"); alert(a); return a + 2; })
//html seajs.use(["a.js"], function(result) { alert(result); })
请问发现了什么问题.答案是a模块与b模块发生了循环引用.这个问题如果在seajs早期的版本里,貌似会报异常,但是现在不会了.当html页面请求a模块时,a模块发现其依赖于b模块,于是执行b模块.但是此时发现其又依赖a模块.seajs现在的处理是直接返回一个空对象{},所以先是弹出[object Object],然后弹出[object Object]2,最后弹出[object Object]21.
四.脚本改写
现有其它类库是不能直接被seajs引用的,而是需要根据SeaJS的的模块定义规则对现有库进行一个封装.以官方封装的jquery1.7.1为例:
(function(factory) { if (typeof define === 'function') { define('#jquery/1.7.1/jquery', [], factory); } else { factory(); } })(function(require) { //jquery原生代码 if (require) return $.noConflict(true); });
它的封装规则,就是在jquery原生代码的上下加上特定代码.可以看到当typeof define == 'function',则说明当前js环境为seajs环境,使用define方法进行封装,否则直接调用传入的factory匿名函数.
对于匿名函数,如果是seajs环境,则传入的require变量不为空,则就回调用jquery的noConflict方法.该方法可以让你自定义jquery的控制变量.
这样封装的好处理,如果没有seajs环境,则可以像普通写法一样在script标签引入脚本,如果是seajs环境,则通过seajs引入.同一个脚本满足了两个执行环境.
五.调试模式
seajs还提供了一个官方插件plugin-map.js,它提供了seajs的在线本地调试模式.当你把它放在seajs的同级目录下,然后在URL后面加上?seajs-debug参数时,窗口的右下脚就会出现一个输入框让你输入需要调试的脚本的路径.点击刷新后,网站脚本的来源就变成你设定的来源了.
以上就是我研究seajs的心得.在研究的过程中参考了作者的官方参考与园友的研究成果,在这里一并感谢了.也祝愿玉伯这个少有的得到众多认可的国产框架写的越来越好:)
参考的文章
1.从 RequireJS 到 SeaJS (1), (2), (3), (4), (5) (可能需要FQ)
2.在线本地调试大观 (可能需要FQ)
3.jQuery 插件的模块化 (可能需要FQ)
4.seajs官方
8.SeaJS快速入门,让js代码模块化 - 2011-09-09修订,新增参考资料
13.Javascript文件加载:LABjs和RequireJS
14.labJS 介绍
15.LABjs
16.我的模块加载系统 v3
17.工程化前端开发
18.js模块化开发---js大项目代码组织和多人协作的解决之道
19.通用前端开发框架(一)