ES6模块与CommonJS模块

ES6 模块与 CommonJS 模块的区别

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。(因为 CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。)
  • CommonJS 模块的 require()是同步加载模块,ES6 模块的 import 命令是异步加载,有一个独立模块依赖的解析阶段。

ES6 模块输出的是值的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// lib.mjs
let count = 1

setTimeout(() => {
count = count + 1
}, 1000)

export { count }

// index.mjs
import { count } from './lib.mjs'

console.log('count', count) // 1

setTimeout(() => {
console.log('timeout count', count) // 2
}, 1100)

count = 1000 // TypeError: Assignment to constant variable.

CommonJS 模块输出的是一个值的拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// lib.cjs
let count = 1

setTimeout(() => {
count = count + 1
}, 1000)

module.exports = { count }

// index.cjs
let { count } = require('./lib.cjs')

console.log('count', count) // 1

setTimeout(() => {
console.log('timeout count', count) // 1000
}, 1100)

count = 1000
  • 从 Node.js v13.2 版本开始,Node.js 已经默认打开了 ES6 模块支持
  • Node.js 要求 ES6 模块采用.mjs 后缀文件名
  • commonjs 模块采用.cjs 后缀文件名

循环引用

“循环加载”(circular dependency)指的是,a 脚本的执行依赖 b 脚本,而 b 脚本的执行又依赖 a 脚本。

CommonJS 模块的循环加载

CommonJS 模块的脚本代码在 require 的时候,就会全部执行,一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。

ES6 模块的循环加载

ES6 模块遇到模块加载命令 import 时,不会去执行模块,而是只生成一个引用。等到真的需要用到时,再到模块里面去取值,不存在缓存值的问题,而且模块里面的变量,绑定其所在的模块。

参考