本文已参与「新人创作礼」活动,一起开启掘金创作之路。详情
我们在学习js的过程中,经常会见到多种模块化规范,在此就来详细讲解一下这些规范都是什么和其之间的区别。
一、CommonJS
在ES6之前,没有原生方法支持模块化。所以需要有特定的规范来进行模块化语法的编写,并且需要模块化的工具把这些模块化的语法与js运行时连接起来。
Commonjs规范了同步声明依赖的模块定义。这个规范主要用于nodejs服务端实现模块化。CommonJS模块语法不能在浏览器中直接运行。
语法示例:
// 1. 使用require()指定依赖
var moduleA = require('./moduleA.js')
// 2. 使用exports
module.exports = {
a: moduleA.name,
tag: 'xxx'
}
特点:
- require()引入依赖是同步的,支持动态依赖;
if (condition) {
requrie('./moduleA.js')
}
只有当condition为true时,moduleA模块才会被加载,这个加载是同步的。
- 模块永远是单例的:无论一个模块被require多少次,实际只加载一次。模块第一次加载后会被缓存,后续其他的加载会直接使用缓存。
var moduleA = require('./a.js')
var moduleB = require('./a.js')
console.log(moduleA)
console.log(moduleA===moduleB) // true
moduleA.name = '小明'
console.log(moduleB.name) // 小明
CommonJS语法不适合于浏览器环境,因为该加载模块的方法是同步的。在服务端所有的模块都存放在本地硬盘,可以同步加载完成,加载的时间就是硬盘读取时间。但是在浏览器中使用该方法加载模块时,因为模块文件都在服务端,要等待网络请求返回文件,需要很长时间,浏览器处于"假死"状态。
因此浏览器的模块加载,就需要异步加载,就出现了AMD。
二、AMD
AMD(Asynchronous Module Definitio)是以浏览器运行环境为目标的模块异步加载规范。由于不是JavaScript原生支持的,所以使用AMD规范进行页面开发需要用到对应的库函数,也就是requireJS。
AMD实际上是requirejs 在推广过程中对模块定义的规范化产出,提前执行,推崇依赖前置
AMD中使用define方法用于定义模块,它是全局变量
define(id?, dependencies?, factory);
参数描述:
- id: 模块的名称。如果没有该参数,模块名默认使用脚本文件名(不带扩展名)。模块名必须是“顶级”的和绝对的(不允许相对名字)。
- 依赖dependencies:是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。如果没有该参数,默认为["require", "exports", "module"]。然而,如果工厂方法的长度属性小于3,AMD加载器会选择以函数的长度属性指定的参数个数调用工厂方法。
- 工厂方法factory,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值。
// 定义模块a
define('a',function() {
// ...
return {
num: 1,
add: funtion() {
this.num++
console.log(this.num)
}
}
})
// 定义模块b
define('b', ['a'], function(a) {
// 使用模块a
a.add();
// ...
return {
name: 'xx',
change: funtion() {
// ...
}
}
})
我们在页面上使用requirejs库进行模块化开发时:
// main.js文件
requirejs.config({
paths: {
jquery: 'jquery.min' //可以省略.js
}
});
//引入模块,用变量$表示jquery模块
requirejs(['jquery'], function ($) {
$('body').css('background-color','red');
});
// a.js文件
// 定义模块
define('a',['jquery'], function ($) {//引入jQuery模块
return {
add: function(x,y){
return x + y;
}
};
});
// 使用
require(['jquery','math'], function ($,math) {
console.log(math.add(10,100));
});
三、CMD
CMD规范规定一个模块就是一个文件。z这个规范明确了模块的基本书写格式和基本交互规则。
CMD规范是国内发展出来的,同样用于浏览器环境的模块化规范。于AMD的区别在于使用的库函数是seaJS
CMD是seajs 在推广过程中对模块定义的规范化产出,延迟执行,推崇依赖就近
define(factory);
factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:require、exports 和 module
// 与AMD规范的define类似
define(function(require, exports, module) {
var a = require('a')
a.dosomething()
// ...
});
使用seajs开发示例:
// 定义模块moduleA.js
define(function(require, exports, module) {
var $ = require('jquery.js')
$('div').addClass('active');
exports.data = 1;
});
// 加载模块
seajs.use(['moduleA.js'], function(a){
var star= a.data;
console.log(star); //1
});
AMD和CMD
我们上面讲到
- AMD推崇依赖前置,即一开始就将模块进入,例如a模块和b模块,然后在回调中拿到该模块使用。
- CMD是按需加载,依赖就近。即在用到某个模块时再require进来。
// AMD写法
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething()
...
})
// CMD写法
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ...
})
- AMD提前执行(异步加载)+延迟执行。CMD延迟执行(按需加载,顺序执行)
- AMD使用requireJS,CMD使用seaJS
- AMD的API根据使用范围有区别,但使用同一个api接口。CMD每个API的职责单一。
四、ES6 Moudle
es6引入了模块规范,使得原生浏览器支持模块化开发。这就意味着之前的加载器和其他预处理都不再必要了。
// 模块引入
import ...
// 模块导出
export ...
ES6的模块导入导出都不允许放在条件语句中,其导入和导出的顺序也应当注意
// 正确导入
import a from 'a.js'
console.log(a)
// 可以,但应该尽量避免
console.log(a)
import a from 'a.js'
// 条件判断中不允许
if(condition) {
import ..
}
// 正确导出
const num = 1
export num
// 可以,但应该尽量避免
export num
const num = 1
// 条件判断中不允许
if(condition) {
export ..
}
总结
- CommonJs:是服务端规范,同步加载。也就是需要模块加载完成后执行后面的代码。
- AMD:
requirejs在推广过程中对模块定义的规范化产出,依赖前置,异步加载。 - CMD:
seajs在推广过程中对模块定义的规范化产出,延迟执行,推崇依赖就近 - ES6 Module:模块输出的是一个值的引用,编译时输出接口,
ES6模块不是对象,它对外接口只是一种静态定义,在代码静态解析阶段就会生成。
区别描述
CommonJs是单个值导出,模块输出的是一个值的copy,运行时加载,加载的是一个对象(module.exports属性),该对象只有在脚本运行完才会生成- 导出:
AMD推荐的风格通过返回一个对象做为模块对象,CommonJS的风格通过对module.exports或exports的属性赋值来达到暴露模块对象的目的
CommonJs是动态语法可以写在判断里,ES6 Module静态语法只能写在顶层CommonJs的this是当前模块,ES6 Module的this是undefinedAMD和CMD是动态引入,运行时才知道的ES6 Module是静态引入,好处方便wepback打包依赖图谱分析CommonJs是单个值导出,ES6 Module可以导出多个