模块化
AMD
AMD规范采用异步方式
加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。这里介绍用require.js实现AMD规范的模块化:用require.config()指定引用路径等,用definde()定义模块,用require()加载模块。
CMD
UMD
CommonJs
CommonJS 使用 module.exports、exports 导出;使用 require 导入
Node.js 是 commonJS 规范的主要实践者。Node 应用由模块组成,采用 CommonJS 模块规范。
每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
// 在node环境直接打印 module 对象
console.log(module)
Module {
id: '.',
path: '',
exports: {}, // 表示模块对外输出的值
filename: '',
loaded: false,
children: [],
paths: []
}
导出
c1.js文件
module.exports.a = 10
module.exports = {
b: 20
}
为了方便,Node为每个模块提供一个 exports 变量,指向 module.exports。
module.exports.a = 10
console.log(module, module.exports, exports)
// module
Module {
id: '.',
path: '',
exports: { a: 10 },
filename: '',
loaded: false,
children: [],
paths: []
}
// module.exports
{ a: 10 }
// exports
{ a: 10 }
因此可以写成
exports.a = 10
需要注意的是,不能将 exports 变量指向一个值,因为那样就等同于切断了exports 和 module.exports 的联系; 同样如果使用 module.exports = {} 的形式赋值,也会丢失 exports 导出的内容。看下面这种情况
module.exports.a = 10 // { a: 10 }
exports.b = 20 // { a: 10, b: 20 }
module.exports = { // { c: 30 }
c: 30
}
导入
const a = require('./c1') // { c: 30 }
const c = require('./c1').c // 30
ESModule
ES Module 采用 export、export default 导出,使用 import 关键字导入
导出
// 第一种 export
export const func = () => {
console.log('这是func')
}
const b = () => {
return 20
}
// 第二种 export default
export default {
a: 10,
b
}
导入
// 第一种
import { func } from './e1.js'
func() // 这是func
// 第二种
import exdata from './e1.js'
console.log(exdata.a) // 10
console.log(exdata.b()) // 20
CommonJs和ESModule的区别
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
运行时加载
:commonjs模块是一个对象;在输入时先加载整个模块,生成一个对象,然后再从这个对象上读取属性和方法。也这样理解:Commonjs加载的是一个对象(module.exports属性),该对象只有在脚本运行时才能生成。
编译时加载(静态加载)
:ES6模块不是对象;是通过export命令指定输出的代码,import时指定加载某个导出的值,而不是加载整个模块。这种加载方式的好处在于加载效率高,能进一步拓展JavaScript的语法,比如引入宏和类型校验这些只能靠静态分析实现的功能。包括tree shaking的实现,也是依赖es6 module的静态加载。
- commonjs是导出值的拷贝,es6 module是导出值的引用
commonjs导出值拷贝
:commonjs的一个模块就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象(拷贝)。

以后需要用到这个模块的时候,就会在exports属性上面取值,即使再次执行require命令,也不会再次执行该模块,而是到缓存之中取值,也就是说commonjs模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载就返回第一次运行的结果。
es导出值引用
:es模块遇到模块加载命令import,就会生成一个只读引用,等到脚本执行时,再根据这个只读引用,到被加载的那个模块里面去取值,原始值变了,import加载的值也会跟着变。因此,es6模块是动态引用,不会有缓存值,模块里的变量绑定其所在的模块。
