是export还是module.exports,是import还是require,是ES6还是CommonJS,是爱❤️还是责任?

故事要从一次错误的编码开始。。。

Node的模块

众所周知,在上古年代,node的开发一直被 Commonjs 规范所支配着,这也是悲剧发生的导火索,请看灾难现场:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// version.js
var version = 3;
function incVersion() {
version++;
}
module.exports = {
version,
incVersion,
};

// main.js
var mod = require('./version');
console.log(mod.version); // 3
mod.incVersion();
console.log(mod.version); // 3

这,这一瞬间,时间的涡轮好像停止了一样,版本号变量version一直停留在3永不向前,就好像我的人生那样,停滞不前,是命运的捉弄,还是人为的操控呢?

不,是值的拷贝,CommonJS 模块输出的是值的拷贝,也就是说,mod.version是一个原始类型的值,会被缓存,那么我们怎样解决这个问题呢?

1
2
3
4
5
6
7
8
9
10
11
# 可以改写成函数
var version = 3;
function incVersion() {
version++;
}
module.exports = {
get version() {
return version
},
incVersion,
};

或者用es6的import引用,那么这又是什么呢,敲黑板,画重点,要考。

ES6 模块与 CommonJS 模块

它们有两个重大差异。

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

CommonJS ES6 特征
exports x 是 module.exports 的一个引用
module.exports x module.exports = xxx,就是导出xxx
export x 多个
export default x 单个
require 导出的内容是module.exports的指向的内存块内容/ es6时是一个对象({default:xxx})
import x 引用多个
1
2
3
4
5
6
7
8
9
10
11
12
//无聊的冷门尝试 es6试用require
//a.js
export default 123
//main.js
const a = require('./a')
console.log('a', a) //{default: 123}

//a.js
export const a = 123
//main.js
const a = require('./a')
console.log('a', a) //{a: 123}

exports 和 module.exports

在一个node执行一个文件时,会给这个文件内生成一个 exports和module对象,
而module又有一个exports属性。他们之间的关系如下图,都指向一块{}内存区域。

1
exports = module.exports = {};

export 和 export default

首先我们讲这两个导出,下面我们讲讲它们的区别

  1. exportexport default均可用于导出常量、函数、文件、模块等
  2. 在一个文件或模块中,exportimport可以有多个,export default仅有一个
  3. 通过export方式导出,在导入时要加{},export default则不需要
  4. export能直接导出变量表达式,export default不行。

天坑-循环依赖

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

1
2
3
4
5
6
7
// a.js
var b = require('b'); //a-step 1
module.exports.value = 'a'; //a-step 2

// b.js
var a = require('a'); //b-step 1
module.exports.value = 'b'; //b-step 2

一般很少会这样写,出现的话证明你的功能逻辑有耦合了,应该先分开,但是一些大型项目就很难避免,小弟不才,真的遇到过。

node的执行顺序

a-step 1 –> b-step 1 –> b-step 2 –> a-step 2

当年的黑科技临时解决办法,其实不建议,就是将a中的a-step 2移到a-step 1之上。

1
2
3
 // a.js
module.exports.value = 'a'; //a-step 2
var b = require('b'); //a-step 1


上面的其实是Commonjs版的,es6的解决方法有点不一样,具体就是将要引用的变量改成函数,因为函数具有提升作用,在加载时就已经有定义了。(ps:改成了函数表达式也不行,因为不具有提升作用)

最后

那么在座各位天才小儿童,肯定能想到怎样用es6解决那个灾难现场了吧,答案就是直接用

1
2
3
4
5
6
7
8
9
10
11
12
//a.js
var version = 3;
function incVersion() {
version ++;
}
export { version, incVersion }

//main.js
import { version, incVersion } from './a'
console.log( 'version', version )
incVersion()
console.log( 'version', version )

XiuerOld wechat
今天扫码,有机会分配一位小哥哥,在线教前端。
请不要给我打赏!