特点
1.解决命名污染,全局污染,变量冲突等
2.内聚私有,变量不能被外部访问
3.更好的分离,按需加载
4.引入其他模块可能存在循环引用问题
5.代码抽象,封装,复用和管理
6.避免通过script标签从上至下加载资源
7.大型项目资源难以维护,特别是多人合作的情况

CommonJS

服务端解决方案。加载速度快(因为模块文件一般存在本地硬盘)
每个文件是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

  • 运行时加载,只能在运行时才能确定一些东西
  • 同步加载,只有加载完成后,才能执行后续操作。因为用于服务端,文件都在本地,同步导入即使卡住主线程影
    响也不大。
  • 导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,所以如果想更新值,必须重新导入一次。
  • 模块在首次执行后会缓存,再次加载只返回缓存结果,若想再次执行,可清除缓存。
  • 模块加载的顺序就是代码出现的顺序

基本语法

  • 暴露模块:module.exports=value或exports.xxx=value
  • 引入模块:require(xx),如果是第三方模块,x为模块名;如果是自定义模块,xx为模块文件路径

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports/属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module..exports/属性。

1
2
3
4
5
6
7
8
9
10
11
//加载模块
var example=require('./example.js');
var config=require(config.js');
var http=require('http');
//对外暴露模块
module.exports.example = function ()
}
module.exports = function(x)
console.log(x)
}
exports.xxx=value;

require命令用于加载模块文件。require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。
因为nodejs主要用于服务器编程,模块文件一般已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以使用CommonJS规范。
如果是浏览器环境,要从服务器端加载模块,用CommonJS需要等模块下载完并运行后才能使用,将阻塞后面代码
的执行,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范,解决异步加载的问题。
AMD
会编译成require/exports来执行
浏览器一般使用AMD规范,解决异步加载问题。
RequireJS是一个工具库。主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范。
Require.js的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。
基本语法
定义暴露模块:

1
2
3
4
5
6
7
8
//定义没有依赖的模块
define(function(){
return 模块
})
//定义有依赖的模块
define(['module1','module2'],function(m1,m2){
return 模块
)

引入使用模块:

1
2
3
4
5
require(['module1','module2'],function(m1,m2){

使用m1/m2

}
  • 采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回
    调函数里,等到加载完成后再执行回调函数;也可以根据需要动态加载模块
  • AMD模块定义的方法非常清晰,不会污染全局环境,可清楚地显示依赖关系

CMD

CMD规范专门用于浏览器端,异步加载模块,实用模块时才会加载执行。
整个了CommonJS和AMD规范的特点。
Sea.js中,所有JS模块都遵循CMD模块定义规范。

基本语法

定义暴露模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义没有依赖的模块
define(function(require,exports,module){
exports.xxx value
module.exports value
})
//定义有依赖的模块
define(function(require,exports,module){
//引入依赖模块(同步)
var module2 require('./module2')
//引入依赖模块(异步)
require.async('./module3',function (m3){
})
//暴露模块
exports.xxx value
}

引入使用模块:

1
2
3
4
5
6
define(function (require){
var m1=require('./module1')
var m4=require('./module4')
m1.show()
m4.show()
})

ES6

使用import和export的形式来导入导出模块。这种方案和上面三种方案都不同。
思想是尽量静态化,为了保证在编译时就能确定模块的依敕关系和输入输出的变量。
异步导入,因为用于浏览器,需要下载文件,如果采用同步导入会对渲染有很大影响
采用实时绑定的方式,导入导出的值都指向同一个内存地址,所以导入值会跟随导出值变化
会编译成require/exports来执行
·使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块。
一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个
变量,就必须使用export关键字输出该变量。
在编译阶段,import会提升到整个模块的头部,首光执行。
如果不需要知道变量名或函数就完成加载,就要用到export defaulti命令,为模块指定默认输出。
·export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只
能使用一次。所以,importi命令后面才不用加大括号,因为只可能唯一对应export default命令。
基本语法
export
1.如果不想对外暴露内部变量的真实名称,可以使用s关键字设置别名,同一个属性可以设置多个别名。
在外部进行引入时,可以通过name2这个变量访问到king值。

1
2
const name='king';
export {name as name2};

2.在同一个文件中,同一个变量名只能够xport一次,否则会抛出异常。

1
2
3
4
const name='king';
const _name='king';
export {name as _name};
export{name};/抛出异常,name作为对外输出的变量,只能export一次

import
1.import和export的变量名相同
2.相同变量名的值只能import-一次
3.import命令具有提升的效果

1
2
3
4
5
//export.js
export const name='king';
//import.js
console.log(name);//king
import {name}from './export.js'

本质:因为import是在编译期间运行的,在执行console语句之前就已经执行了importi语句。因此能够打印出name
的值,即,King
4.多次import时,只会加载一次
以下代码汇总,我们import了两次export.js文件,但是最终只输出执行了一次“开始执行”,可以推断出import导入
的模块是个单例模式。

1
2
3
4
5
6
7
//export.js
console.log('start');
export const name='king';
export const age=19;
//import.js
import {name}from './export.js
import {age}from './export.js''

5.允许我们在需要的时候才动态加载模块,而不是一开始就加载所有模块,这样可以帮我们提高性能。
这个新功能允许我们将import()作为函数调用,将其作为参数传递给模块的路径。它返回一个promise,它用一个
模块对象来实现,可以访问该对象的导出。

1
2
3
4
import('/modules/myModule.mjs')
then((module)=>
/Do something with the module.
});

对比总结
ES6输出值的引用(对外接口只是一种静态定义,代码解读阶段生成),CommonJS模块输出值的拷贝(加载一个
对象,及module.exports.属性,该对象只有在脚本运行时才会生成)
ES6模块时动态引用,且不会缓存值,模块中的变量绑定其所在的模块
ES6模块编译时输出接口,CommonJS模块运行时加载
1.CommonJS规范主要用于服务端编程,同步加载模块,不适合浏览器环境,因为同步意味阻塞,而浏览器资源时
异步加载的,因此诞生了AMD和CMD
2.AMD规范在浏览器环境中异步加载模块,且可以并行加载多个模块。但是开发成本高,代码阅读困难,模块定
义语义不顺畅
3.CMD和AMD相似,依赖就近,延迟执行,易在nod©js运行。但是,依赖SPM打包,模块加载逻辑偏重
4.ES6实现模块功能且实现简单,完全可以取代CommonJS和AMD规范,进而成为浏览器和服务器通用模块解决
方案的笼儿。