前言javascript
模块化开发JS已经逐渐开始普及,在RequireJS和SeaJS中犹豫了2天,最后选择从SeaJS入手.网上有很多教程和使用方法,不过理论东西太多了,我是标准的实用主义和拿来主义者,因此直接用代码说话.
html
正文前端
先上一个最简单的Demo,比官网上的简单无数倍:
java
从官网下载最新版本的Sea.js文件,我下载的是2014年3月19的2.2.1版:https://github.com/seajs/seajs/archive/2.2.1.zip.git
咱们就须要dist中的sea.js便可,接下来在dist文件夹同目录下建立demo.html,util.js,componet_one.js这三个文件.
github
util.js文件中定义一个功能,单独做为一个模块:
前端工程师
//util.js define(function (require,exports,module) { function print (str) { // body... alert(str); } exports.print = print; });
而后定义componet_one.js,它的实现依赖于util.js:异步
//componet_one.js define(function (require,exports,module) { var util = require("./util.js"); var ComponetOne = { doSth : function(){ util.print("I'm a String.") } } return ComponetOne; })
最后在index.html页面上,直接开始调用:async
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>SeaJS</title> </head> <body> <script type="text/javascript" src="dist/sea.js"></script> <script type="text/javascript"> seajs.use("./componet_one.js",function(componet_one){ componet_one.doSth(); }); </script> </body> </html>
页面上显示的结果是弹出一个对话框上面写着:模块化
"I'm a String."
示例完毕,感受如何,我认为即便对模块化或者Sea.js和RequireJS一点概念都没有,只要有必定的Javascript基础都能理解或者说猜出上面代码的功能和内容.做为理解模块化的入门以及Sea.js应该算是最简单的代码了.
代码虽少,不过仍是能够很直观的看出一些东西的,因此经过这几段简单代码来引入一些Sea.js中的API和使用方法.由于Sea.js遵循的是CommonJS规范,因此它的API使用起来虽然看着和RequireJS很像,但仍是有所不一样,而且它们内部实现也不同.
(1) CMD模块定义规范
在上面的util.js中经过define()定义了一个模块,能够看到全局函数define就接受了一个函数参数.其实define还可接受对象或者字符串为参数,这时表示模块的接口就是这对象、字符串.
define({"foo" : "bar}); define("It's a template");
这都是能够的,大多数的状况下仍是以函数为参数,表示模块的构造方法.执行这个构造方法能够获得模块向外提供的接口,好比上面utils.js中就是如此.这种状况下方法执行时,默认传入三个参数:require、exports以及module.
若是你不知道是否能够在当前开发项目中使用define,能够经过define.cmd进行判断当前页面是否有CMD模块加载器:
if (typeof define === "function" && define.cmd){ //此时表示存在Sea.js等CMD模块加载器 }
(2) 经过exports对象向外提供模块接口
在util.js中define定义的方法体内,有一行代码是exports.print = print;什么都不看的解释是将方法体内定义的print函数指向exports的print属性.
exports是define()定义模块时,Function传入的三个默认参数之一,经过它能够对外提供属性或者方法.
除了给exports对象增长成员,也能够经过return直接向外提供接口,将util.js改成return的方式就以下:
define(function (require,exports,module) { return { print : function(str) { // body... alert(str); } } });
还能够经过module.exports进行赋值,module是define()的参数为函数参数时的函数参数的一个函数.这么说有点费劲,那么我对以前说的define进行一点补充.
标准说法的define,应该是define(factory)做为define的参数,factory可使一个函数,也能够是一个对象或者字符串.因此说require、exports和module都是factory的默认参数.
util.js经过module.exports的写法以下,效果是同样的:
define(function (require,exports,module) { module.exports = { print : function(str) { // body... alert(str); } } });
exports仅仅是module.exports的一个引用,在factory内部给exports从新赋值时,不会改变module.exports的值,所以给exports赋值是没有效果的,不能用来改变模块接口.这个地方exports和module.exports的关系和Nodejs中是彻底同样的.
(3) 存储当前模块属性和方法的module
既然上面以及提到了module,这里就接着往下说,module中最有意义也是须要掌握的就是module.exports,这个在上面说exports的时候提到了.传给factory构造方法的exports参数是module.exports对象的一个引用.仅仅经过exports参数提供接口有时没法知足需求.
例如当模块的接口是某个类的实例时,就要经过module.exports来实现:
define(function (require,exports,module) { //exports 是 module.exports 的一个引用 console.log(module.exports === exports); //true module.exports = new SomeClass(); console.log(module.exports === exports); //false });
(4) 经过require获取模块
经过define、exports以及module的介绍相信util.js中的代码是什么意思并不难理解,而后看一下componet_one.js文件,它也是一个经过define定义的模块,在factory中第一行:var util = require("./util.js");
若是以前用过Nodejs,那么对于require就很熟悉了.它经过一个模块标识做为惟一参数,用来获取模块提供的接口.这里就是获取到了util模块,可使用util.js文件中定义的内容,这点没什么疑问.
require还有一个实现就是require.async方法用来在模块内部异步加载模块,并在加载完成后执行回调函数.
define(function(require,exports,module){ //异步加载一个模块,加载完成后执行callback require.async('./b',function(b){ b.dosth(); }); //异步加载多个模块,而后执行callback require.async(['./c','./d'],function(c,d){ c.dosth(); d.dosth(); }); });
结尾
相似RequireJS和SeaJS这类模块加载器,它们的实质是为了弥补Javascript在设计之处的不足,能够想象就连Javascript的发明者也没有预料到今天Javascript的火热和普及程度,因此在设计上没有作好文件组织管理这方面的功能.
由于早期它看起来确实就是一种简单的脚本语言,一个页面也就一个JS文件直接引入就行了.可是随着Javascript火爆的发展以及大量的应用,这明显成为其短板,因此广大优秀的Coder们想出了各类办法进行解决.
模块加载主要目的是令JavaScript开发模块化并能够轻松愉悦进行加载,将前端工程师从繁重的JavaScript文件及对象依赖处理中解放出来,能够专一于代码自己的逻辑.
后续会逐渐写一些更实用的东西,欢迎留言给我,一块儿学习~