🍉 模块化
特点
- 解决命名污染,全局污染,变量冲突等
- 内聚私有,变量不能被外部访问
- 更好的分离,按需加载
- 引入其他模块可能存在循环引用问题
- 代码抽象,封装,复用和管理
- 避免通过script标签从上至下加载资源
- 大型项目资源难以维护,特别是多人合作的情况
CommonJS
服务端解决方案。加载速度快(因为模块文件一般存在本地硬盘)
-
**每个文件是一个模块,**有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
-
运行时加载,只能在运行时才能确定一些东西
-
同步加载,只有加载完成后,才能执行后续操作。 因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。
-
导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,所以如果想更新值,必须重新导入一次。
-
模块在首次执行后会缓存,再次加载只返回缓存结果,若想再次执行,可清除缓存。
-
模块加载的顺序就是代码出现的顺序
基本语法
- 暴露模块:
module.exports = value
或exports.xxx = value
- 引入模块:
require(xxx)
,如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
// 加载模块
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方法,实现代码的模块加载。
基本语法
定义暴露模块:
//定义没有依赖的模块
define(function(){
return 模块
})
//定义有依赖的模块
define(['module1', 'module2'], function(m1, m2){
return 模块
})
引入使用模块:
require(['module1', 'module2'], function(m1, m2){
使用m1/m2
})
- 采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数;也可以根据需要动态加载模块
- AMD模块定义的方法非常清晰,不会污染全局环境,可清楚地显示依赖关系
CMD
CMD规范专门用于浏览器端,异步加载模块,实用模块时才会加载执行。
整个了CommonJS和AMD规范的特点。
Sea.js中,所有JS模块都遵循CMD模块定义规范。
基本语法
定义暴露模块:
//定义没有依赖的模块
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
})
引入使用模块:
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 default
命令,为模块指定默认输出。
- export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。
基本语法
export
1.如果不想对外暴露内部变量的真实名称,可以使用as关键字设置别名,同一个属性可以设置多个别名。
在外部进行引入时,可以通过name2这个变量访问到king值。
const name='king';
export {name as name2};
2.在同一个文件中,同一个变量名只能够export一次,否则会抛出异常。
const name='king';
const _name='king';
export {name as _name};
export {_name};//抛出异常,_name作为对外输出的变量,只能export一次
import
1.import和export的变量名相同
2.相同变量名的值只能import一次
3.import命令具有提升的效果
//export.js
export const name='king';
//import.js
console.log(name);//king
import {name} from './export.js'
本质:因为import是在编译期间运行的,在执行console语句之前就已经执行了import语句。因此能够打印出 name的值,即,King
4.多次import时,只会加载一次
以下代码汇总,我们import了两次export.js文件,但是最终只输出执行了一次“开始执行”,可以推断出import导入的模块是个单例模式。
//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
,它用一个模块对象来实现,可以访问该对象的导出。
import('/modules/myModule.mjs')
.then((module) => {
// Do something with the module.
});
对比总结
ES6 输出值的引用(对外接口只是一种静态定义,代码解读阶段生成),CommonJS模块输出值的拷贝(加载一个对象,及module.exports属性,该对象只有在脚本运行时才会生成)
- ES6模块时动态引用,且不会缓存值,模块中的变量绑定其所在的模块
ES6模块编译时输出接口,CommonJS模块运行时加载
- CommonJS规范主要用于服务端编程,同步加载模块,不适合浏览器环境,因为同步意味阻塞,而浏览器资源时异步加载的,因此诞生了AMD 和 CMD
- AMD规范在浏览器环境中异步加载模块,且可以并行加载多个模块。但是开发成本高,代码阅读困难,模块定义语义不顺畅
- CMD和AMD相似,依赖就近,延迟执行,易在nodejs运行。但是,依赖SPM打包,模块加载逻辑偏重
- ES6实现模块功能且实现简单,完全可以取代CommonJS和AMD规范,进而成为浏览器和服务器通用模块解决方案的宠儿。