模块发展史:一开始人们认为js没什么用,官方定义的API只能构建基于浏览器的应用程序。CommonJS就按耐不住了,CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。
2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志"Javascript模块化编程"正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。NodeJS是CommonJS规范的实现,webpack 也是以CommonJS的形式来书写。
基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。还是上面的代码,如果在浏览器中运行,会有一个很大的问题,你能看出来吗?
var math = require('math');
math.add(2, 3);
第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。
这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。
CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,AMD(异步模块定义)出现了,它就主要为前端JS的表现制定规范。
一、commonJS
1.著名实现:NodeJS、Webpack
2.模块规范
一个文件就是一个模块,拥有单独的作用域。普通方式定义的变量、函数、对象都属于该模块内。
- 通过require加载模块
- 通过 exports 和 modul.exports 来暴露模块中的内容
注:{模块引用(require)} {模块定义(exports)} {模块标识(module)}
require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。
3.使用 exports 暴露模块接口
(1)下面我们在 Node.js 中创建一个模块,文件名为:handle.js
exports.hello = function() {
console.log('Hello CommonJS');
}
(2)创建一个main.js文件,引入这个模块并调用
var handle = require('./handle');
handle.hello();
(3)控制台输入node main.js,运行结果:Hello CommonJS
4.使用 modul.exports 暴露模块对象
(1)把一个对象封装到模块中,名为handle.js
//私有变量
var test = 110;
//公开方法
function Handle() {
var name;
this.setName = function(Name) {
name = Name;
};
this.hello = function() {
console.log('Hello ' + name);
};
};
module.exports = Handle;
(2).创建一个 main.js 文件,引入这个模块并调用
var Handle = require('./handle');
var hello = new Handle();
hello.setName('commonJS');
hello.hello();
(3)控制台输入node main.js,运行结果:Hello CommonJS
5.原理
浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量
- module
- exports
- require
- global
只要能够提供这四个变量,浏览器就能加载 CommonJS 模块
示例:
var module = {
exports: {}
};
(function(module, exports) {
exports.multiply = function (n) { return n * 1000 };
}(module, module.exports))
var f = module.exports.multiply;
f(5) // 5000
二、ES2015
1.2015年6月,ES2015(ECMAScript6、ES6)正式发布
2.模块规范
- 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取
- export 命令用于规定模块的对外接口
- import 命令用于输入其他模块提供的功能
3.使用 export命令规定对外接口
(1)下面我们在 Node.js 中创建一个模块,文件名为:handle.js
//圆面积计算
export function area(radius) {
return Math.PI * radius * radius;
}
//圆周长计算
export function circumference(radius) {
return 2 * Math.PI * radius;
}
(2)创建一个 main.js 文件,引入这个模块并调用
import {area,circumference} from './handle';
console.log('圆面积:' + area(10));
console.log('圆周长:' + circumference(11));
当然也可以使用星号(*)指定一个对象,实现模块的整体加载
import * as circle from './handle';
console.log('圆面积:' + circle.area(10));
console.log('圆周长:' + circle.circumference(11));
(3)由于 NodeJS 目前还不支持 ES2015 的 Module,这里我们借助 babel-node 来执行
4.使用 export default 命令来输出模块
(1)下面我们使用 export default 命令用于指定模块的默认输出。模块文件名为:handle.js
//圆面积计算(作为默认接口)
export default function(radius) {
return Math.PI * radius * radius;
}
//圆周长计算
export function circumference(radius) {
return 2 * Math.PI * radius;
}
(2)创建一个 main.js 文件,引入这个模块并调用。注意:对于 export default指定模块的默认输出,import 语句不需要使用大括号
import area, {circumference} from './handle';
console.log('圆面积:' + area(10));
console.log('圆周长:' + circumference(11));
(3)同样使用babel-node运行
三、AMD
1.基本介绍
- AMD 全称为 Asynchromous Module Definition(异步模块定义)
- AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,它是一个在浏览器端模块化开发的规范
- require.js的诞生,就是为了解决这两个问题:
- 实现js文件的异步加载,避免网页失去响应;
- 管理模块之间的依赖性,便于代码的编写和维护。
- AMD 模式可以用于浏览器环境并且允许非同步加载模块,也可以按需动态加载模块
2.模块规范
-
AMD 通过异步加载模块。模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中
-
AMD 规范只定义了一个函数 define,通过 define 方法定义模块。该函数的描述如下:
define(id?, dependencies?, factory) id:指定义中模块的名字(可选)。如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。如果提供了该参数,模块名必须是“顶级”的和绝对的(不允许相对名字)。 dependencies:当前模块依赖的,已被模块定义的模块标识的数组字面量(可选)。 factory:一个需要进行实例化的函数或者一个对象。
-
AMD 规范允许输出模块兼容 CommonJS 规范,这时 define 方法如下:
define(function (require, exports, module) { var reqModule = require("./someModule"); requModule.test(); exports.asplode = function () { //someing } });
-
require([module], callback);//
3.独立模块
(1)我们使用 RequireJS 定义一个不依赖其他模块得独立模块,文件名:handle.js
define(function(){
var add = function(x,y) {
return x + y;
};
return {
add : add
}
});
(2)接着创建一个 html 页面,其内部加载并调用这个模块
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="require.js"></script>
<script type="text/javascript">
require(['handle'], function (m){
console.log(m.add(2,3));
});
</script>
</head>
<body>
</body>
</html>
(3)控制台输出5
4.存在依赖的函数式定义
下面定义的模块又依赖于 cart 和 inventory 这两个模块(它们都处在同一个文件夹下)
define(["./cart", "./inventory"], function(cart, inventory) {
//return an object to define the "my/shirt" module.
return {
color: "blue",
size: "large",
addToCart: function() {
inventory.decrement(this);
cart.add(this);
}
}
}
);
四、CMD
1.基本介绍
(1)CMD 全称为 Common Module Definition,它是国内玉伯大神在开发 SeaJS 的时候提出来的
(2)CMD 与 AMD 挺相近,二者区别如下:
- 对于依赖的模块 CMD 是延迟执行,而 AMD 是提前执行
- CMD 推崇依赖就近,AMD 推崇依赖前置
- AMD 的 api 默认是一个当多个用,CMD 严格的区分推崇职责单一,其每个 API 都简单纯粹。例如:AMD 里 require 分全局的和局部的。CMD 里面没有全局的 require,提供 seajs.use() 来实现模块系统的加载启动
2.使用 exports 暴露模块接口
(1)下面使用 sea.js 创建一个模块,文件名为:handle.js
define(function(require, exports) {
// 对外提供name属性
exports.name = 'hangge';
// 对外提供hello方法
exports.hello = function() {
console.log('Hello sea.js');
};
});
(2)接着创建一个 html 页面,其内部加载并调用这个模块
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="sea.js"></script>
<script type="text/javascript">
//加载一个模块,在加载完成时,执行回调
seajs.use('handle', function(a) {
a.hello();
});
</script>
</head>
<body>
</body>
</html>
(3)控制台输出: Hello sea.js
3.使用 modul.exports 暴露模块对象
(1)下面我们把一个对象封装到模块中,文件名为:handle.js
define(function(require, exports, module) {
// 对外提供接口
module.exports = {
name: 'hangge',
hello: function() {
console.log('Hello sea.js');
}
};
});
(2)接着创建一个 html 页面,其内部加载并调用这个模块
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="sea.js"></script>
<script type="text/javascript">
//加载一个模块,在加载完成时,执行回调
seajs.use('handle', function(a) {
a.hello();
});
</script>
</head>
<body>
</body>
</html>
(3)控制台输出: Hello sea.js