什么是模块化开发?
一个模块就是实现特定功能的文件,有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。模块开发需要遵循一定的规范,否则就都乱套了。目前,通行的js模块规范主要有两种:CommonJS和AMD。
AMD规范
AMD 即Asynchronous Module Definition,中文名是“异步模块定义”的意思。它是一个在浏览器端模块化开发的规范,服务器端的规范是CommonJS模块将被异步加载,模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中。AMD 是 RequireJS 在推广过程中对模块定义的规范化的产出。
define() 函数
AMD规范只定义了一个函数 define,它是全局变量。函数的描述为:1
define(id?, dependencies?, factory);
使用 require 和 exports
创建一个名为”alpha”的模块,使用了require,exports,和名为”beta”的模块:1
2
3
4
5
6
7define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
//Or:
return require("beta").verb();
}
});
目前,实现AMD的库有RequireJS 、curl 、Dojo 、Nodules 等。
CommonJS规范
CommonJS是服务器端模块的规范,Node.js采用了这个规范。Node.JS首先采用了js模块化的概念。根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。输出模块变量的最好方法是使用module.exports对象。1
2
3
4
5
6
7
8
9var i = 1;
var max = 30;
module.exports = function () {
for (i -= 1; i++ < max; ) {
console.log(i);
}
max *= 1.1;
};
上面代码通过module.exports对象,定义了一个函数,该函数就是模块外部与内部通信的桥梁。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的module.exports对象。
RequireJS和SeaJS
- RequireJS由James Burke创建,他也是AMD规范的创始人。
- define方法用于定义模块,RequireJS要求每个模块放在一个单独的文件里。
- RequireJS 和 Sea.js 都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。
SeaJS与RequireJS最大的区别:
SeaJS对模块的态度是懒执行, 而RequireJS对模块的态度是预执行
不明白?看这篇图文并茂的文章吧:http://www.douban.com/note/283566440/
个人推荐requireJs,被依赖的模块必须先于当前模块执行,而没有依赖关系的模块,可以没有先后。假设依赖模块mod1和mod2某天发生了依赖的话,比如在某个版本,mod1依赖了mod2(这是完全有可能的),mod1比mod2先执行。这个时候seajs的懒执行会出现问题。
为什么要用requireJS
试想一下,如果一个网页有很多的js文件,那么浏览器在下载该页面的时候会先加载js文件,从而停止了网页的渲染,如果文件越多,浏览器可能失去响应。其次,要保证js文件的依赖性,依赖性最大的模块(文件)要放在最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
RequireJS就是为了解决这两个问题而诞生的:
- 实现js文件的异步加载,避免网页失去响应;
- 管理模块之间的依赖性,便于代码的编写和维护。
AMD和CMD
CMD(Common Module Definition) 通用模块定义。该规范明确了模块的基本书写格式和基本交互规则。该规范是在国内发展出来的。AMD是依赖关系前置,CMD是按需加载。
在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:1
define(factory);
factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:require、exports 和 module:1
2
3
4
5define(function(require, exports, module) {
// 模块代码
});
require是可以把其他模块导入进来的一个参数,而export是可以把模块内的一些属性和方法导出的。
- AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
- CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。
- AMD:提前执行(异步加载:依赖先执行)+延迟执行
- CMD:延迟执行(运行到需加载,根据顺序执行)
CMD 推崇依赖就近,AMD 推崇依赖前置。看如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ...
})
// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething()
...
})
AMD的优点是:异步并行加载,在AMD的规范下,同时异步加载是不会产生错误的。CMD的机制则不同,这种加载方式会产生错误,如果能规范化模块内容形式,也可以.
jquery1.7以上版本会自动模块化,支持AMD模式:主要是使用define函数,sea.js虽然是CommonJS规范,但却使用了define来定义模块
define函数和AMD的define类似:1
2
3
4
5
6
7
8
9
10
11
12
13define(function(require, exports, module{
//先要载入jQuery的模块
var $ = require('jquery');
//然后将jQuery对象传给插件模块
require('./cookie')($);
//开始使用 $.cookie方法
});
sea.js如何使用?
1 | - 引入sea.js的库 |
requirejs开发
require.config是用来定义别名的,在paths属性下配置别名。然后通过requirejs(参数一,参数二);参数一是数组,传入我们需要引用的模块名,第二个参数是个回调函数,回调函数传入一个变量,代替刚才所引入的模块。
main.js文件1
2
3
4
5
6
7
8
9
10//别名配置
requirejs.config({
paths: {
jquery: 'jquery.min' //可以省略.js
}
});
//引入模块,用变量$表示jquery模块
requirejs(['jquery'], function ($) {
$('body').css('background-color','red');
});
引入模块也可以只写require()。requirejs通过define()定义模块,定义的参数上同。在此模块内的方法和变量外部是无法访问的,只有通过return返回才行.
define 模块
1 | define(['jquery'], function ($) {//引入jQuery模块 |
将该模块命名为math.js保存。
main.js引入模块方法
1 | require(['jquery','math'], function ($,math) { |
AMD推荐的风格通过返回一个对象做为模块对象,CommonJS的风格通过对module.exports或exports的属性赋值来达到暴露模块对象的目的。
附图: