一、模块化的概念
为了解决命名冲突和文件依赖等问题,提出了模块化的概念,即将一个实现特定功能的文件定义为一个模块,我们可以通过模块加载,轻松的使用已经写好的代码。
二、CMD与AMD规范
javascript模块化编程主要有CMD和AMD规范。
CMD:就近加载模块,当需要使用的时候才去加载;
AMD:异步加载模块,依赖前置(需要按顺序把需要用到的模块都加载好),加载好的依赖包都被提前分析好,后面的程序直接使用即可。
1.CommonJs(CMD)通用模块定义:
- 定义模块:一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,即在该模块内部定义的变量,无法被其他模块访问,除非定义为global对象的属性。
- 模块输出: 模块只有一个出口,module.exports对象,我们需要把模块希望输出的内容放入该对象中,给其他模块访问。
- 加载模块: 加载模块使用require()方法,该方法读取一个文件并执行,返回文件内部的module.exports对象。
- CMD的主要代表是SeaJS
2.AMD异步模块定义:
AMD主要为了解决两个问题:
- 多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
- 加载js文件的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长
- AMD的代表是RequireJS
三、SeaJS
seaJS遵循CMD模块定义规范,一个模块就是一个文件。
1.加载文件
在加载seajs时,可以通过给<script>便签添加@data-main属性,来指定页面的初始脚本。
<script src="sea.ja" data-main="./main"></script>
./为相对路径,“./main”在seajs中被称作模块ID(后面会讲到)
2.SeaJS配置
在SeaJS中使用config()来配置基本的模块信息
seajs.config({
base: "../url", //基本路径
alise: {"jquery": "jquery/jquery.js"} //给文件起别名
});
``
3.模块定义
语法:define("moduleNmae"?,function(require,exports,module) {
...
})`;
moduleName是模块的名称,可以不给定,为了模块的可移植性,一般不给定;
require是一个方法,接受模块标志作为唯一参数,用来获取其他模块提供的接口;
exports是一个对象,用来向外提供模块的接口;
module是一个对象,存储了与当前模块相关的一些属性和方法。
4.模块启动
在SeaJS中,使用seajs.use()来启动已经定义好的模块。
语法:seajs.use(uel,callback);
url:调用模块的地址,url默认的根目录是SeaJS所有的文件夹。填入url后,SeaJS会异步加载url指定的文件,加载完成后执行callback函数,这样就实现了按需加载模块的目的。(callback函数时exports对象)
seajs.use(['./module1','module2'],function(module1,module2) {
module1.init();
module2.init();
})
5.模块标识
模块标识分为相对标识和绝对标识
相对标识:以“.”开头,只出现在模块坏境中(define的factory方法里面),永远相对当前的uri来解析。
顶级标识:不以点(.)或者斜线(/)开始,会相对模块系统的基础路径(即SeaJS的base路径)来解析。
如果使用path/moduleName来指定模块,SeaJS将会使用“base路径+模块ID—+.js”这一规则来拼接模块的实际地址;如果模块ID不以点(.)其实,SeaJS则使用base作为基本路径来拼接模块的实际地址。如果没有配置base路径,则默认当前页的上级目录为base路径。
6.SeaJS书写规范
- 模块factory构造方法的第一个参数必须命名为require
define(function(require) {}); - 不要重命名require函数,或在任何作用域中给require重新赋值;
- require的参数必须是字符串
reuqure(“moduleName”);
原因:由于SeaJS是就近加载模块,在页面使用seajs.use()启用一个模块的时候或者模块内部require()时,无法获取或计算变量的值;
四、requireJS
1.加载require脚本
<script src="require.js" data-main="main.js"></script>
require在加载时会去检查data-main属性的值,并将其设为启动脚本
2.定义模块
requireJs模块的好处是,无需全局地引用其他模块。
- 如果一个模块仅含有键值对,没有任何以来,则在define()中定义这些键值对即可;
define({
key1: "value1",
key2: "value2"
});
+没有任何依赖的函数定义
define(function() {...});
- 模块存在依赖
define(['module1','module2'],function(module1,module2) {...});
第一个参数是依赖的名称数组;第二个参数是一个函数;
在模块的所有依赖加载完毕后,该函数就会被调用来定义该模块,因此该模块应该返回一个定义了本模块的object;(对模块的返回值类型没有强制一定是一个Object,任何函数的返回值都是允许的)
依赖关系会以参数的形式注入到该函数中,参数列表与依赖关系是一一对应的。
3。模块名
- 我们可以显示的指定模块的名称,但这使得模块不具备移植性,也就是说,如果你将文件移动到其他目录下,你就必须重新命名。
- define()中的相对模块名
为了可以再define()内部使用类似 require(“./relative/moduleName”); 的方式加载模块,并正确解析相对名称,需要将“require”本身作为一个依赖,注入到模块中。
define(['require','./relative/moduleName'],function(require) {
var module1 = require("./relative/moduleName");
});
或者可以使用为CommonJs所设的更短语法:
define(function(require) {
var module1 = require("./relative/moduleName");
});
相对路径在一些场景中格外有用。例如:为了以便将代码分享给其他人或者项目,你在某个目录下创建了一些模块你可以访问模块的相邻模块,而无需知道该目录的名称。
6.循环依赖
如果有一个循环依赖(模块one和模块two相互依赖),在这种情况下,当模块two的函数被调用时,它会得到一个undefined的one,这时,two可以再模块已经定义好后,用require()放啊发再获取。(需将require作为依赖注入进去)。
define(['require','one'],function(require,two) {
//在这里,one为undefined
return {
require("one").oneDoSomething();
}
});
参考文章
RequireJS中文网:http://www.requirejs.cn/
饥人谷前端进阶模块化: