特点
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 | //加载模块 |
require命令用于加载模块文件。require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。
因为nodejs主要用于服务器编程,模块文件一般已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以使用CommonJS规范。
如果是浏览器环境,要从服务器端加载模块,用CommonJS需要等模块下载完并运行后才能使用,将阻塞后面代码
的执行,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范,解决异步加载的问题。
AMD
会编译成require/exports来执行
浏览器一般使用AMD规范,解决异步加载问题。
RequireJS是一个工具库。主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范。
Require.js的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。
基本语法
定义暴露模块:
1 | //定义没有依赖的模块 |
引入使用模块:
1 | require(['module1','module2'],function(m1,m2){ |
- 采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回
调函数里,等到加载完成后再执行回调函数;也可以根据需要动态加载模块 - AMD模块定义的方法非常清晰,不会污染全局环境,可清楚地显示依赖关系
CMD
CMD规范专门用于浏览器端,异步加载模块,实用模块时才会加载执行。
整个了CommonJS和AMD规范的特点。
Sea.js中,所有JS模块都遵循CMD模块定义规范。
基本语法
定义暴露模块:
1 | //定义没有依赖的模块 |
引入使用模块:
1 | define(function (require){ |
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 | const name='king'; |
2.在同一个文件中,同一个变量名只能够xport一次,否则会抛出异常。
1 | const name='king'; |
import
1.import和export的变量名相同
2.相同变量名的值只能import-一次
3.import命令具有提升的效果
1 | //export.js |
本质:因为import是在编译期间运行的,在执行console语句之前就已经执行了importi语句。因此能够打印出name
的值,即,King
4.多次import时,只会加载一次
以下代码汇总,我们import了两次export.js文件,但是最终只输出执行了一次“开始执行”,可以推断出import导入
的模块是个单例模式。
1 | //export.js |
5.允许我们在需要的时候才动态加载模块,而不是一开始就加载所有模块,这样可以帮我们提高性能。
这个新功能允许我们将import()作为函数调用,将其作为参数传递给模块的路径。它返回一个promise,它用一个
模块对象来实现,可以访问该对象的导出。
1 | import('/modules/myModule.mjs') |
对比总结
ES6输出值的引用(对外接口只是一种静态定义,代码解读阶段生成),CommonJS模块输出值的拷贝(加载一个
对象,及module.exports.属性,该对象只有在脚本运行时才会生成)
ES6模块时动态引用,且不会缓存值,模块中的变量绑定其所在的模块
ES6模块编译时输出接口,CommonJS模块运行时加载
1.CommonJS规范主要用于服务端编程,同步加载模块,不适合浏览器环境,因为同步意味阻塞,而浏览器资源时
异步加载的,因此诞生了AMD和CMD
2.AMD规范在浏览器环境中异步加载模块,且可以并行加载多个模块。但是开发成本高,代码阅读困难,模块定
义语义不顺畅
3.CMD和AMD相似,依赖就近,延迟执行,易在nod©js运行。但是,依赖SPM打包,模块加载逻辑偏重
4.ES6实现模块功能且实现简单,完全可以取代CommonJS和AMD规范,进而成为浏览器和服务器通用模块解决
方案的笼儿。