前端模块化就是复杂的文件编程一个一个独立的模块,比如js文件等等,分成独立的模块有利于重用和维护,这样会引来模块之间相互依赖的问题,变量名重复的问题。所以有了commonJS规范,AMD,CMD规范等等。
早期的模块化
1. 文件划分
一个 script 标签对应一个模块,然后直接调用模块中的成员(变量 / 函数)。
缺点:
- 模块直接在全局工作,大量模块成员污染全局作用域;
- 没有私有空间,所有模块内的成员都可以在模块外部被访问或者修改;
- 一旦模块增多,容易产生命名冲突;
- 无法管理模块与模块之间的依赖关系;
- 在维护的过程中也很难分辨每个成员所属的模块。
2. 命名空间方式
将每个模块“包裹”为一个全局对象的形式实现,这种方式就好像是为模块内的成员添加了“命名空间”。然后还是用script标签引入
// module-a.js
window.moduleA = {
method1: function () {
console.log('moduleA#method1')
}
}
这种命名空间的方式只是解决了命名冲突的问题,但是其它问题依旧存在。
3.IIFE
将每个模块成员都放在一个立即执行函数所形成的私有作用域中,对于需要暴露给外部的成员,通过挂到全局对象上的方式实现。
// module-a.js
(function () {
var name = 'module-a'
function method1 () {
console.log(name + '#method1')
}
window.moduleA = {
method1: method1
}
})()
这种方式带来了私有成员的概念,私有成员只能在模块成员内通过闭包的形式访问,这就解决了前面所提到的全局作用域污染和命名冲突的问题。
4.
模块化规范
CommonJS 规范,它是 Node.js 中所遵循的模块规范,以同步的方式加载模块,
如果要在浏览器端使用同步的加载模式,就会引起大量的同步模式请求,导致应用运行效率低下。
所以早期浏览器使用了AMD规范,即异步模块定义规范,RequireJS库。
同期出现的规范还有淘宝的 Sea.js,只不过它实现的是另外一个标准,叫作 CMD,这个标准类似于 CommonJS,在使用上基本和 Require.js 相同,可以算上是重复的轮子。
区别:
AMD是依赖关系前置,在定义模块的时候就要声明其依赖的模块。 CMD是按需加载依赖就近,只有在用到某个模块的时候在去require
AMD/CMD区别,虽然都是并行加载js文件,但还是有所区别,AMD是预加载,在并行加载js文件同时,还会解析执行该模块(因为还需要执行,所以在加载某个模块前,这个模块的依赖模块需要先加载完成);而CMD是懒加载,虽然会一开始就并行加载js文件,但是不会执行,而是在需要的时候才执行。
目前,ES Modules 已发展成为现今最主流的前端模块化标准,Node 环境也会逐渐趋向于 ES Modules 规范。
ES6module加载原理:
js引擎对脚本静态分析的时候,遇到模块加载命令import 就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用到被加载的模块中取值。原始值变了,import加载的值也会变,es6模块是动态引用,并且不会缓存值。
ES6模块与CommonJS模块的差异:
1.CommonJS模块输出的是一个值的复制,ES6模块输出的是一个值的引用 CommonJS模块输出的是一个值的复制,一旦这个值输出以后,模块内部怎么变化就影响不到这个值。
2.CommonJS模块是运行时加载,Es6模块是编译时输出接口。
CommonJS加载的是一个对象(module.exports属性),该对象只有在脚本运行结束时才会生成。Es6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。