exports、module.exports、export 和 export default 的案例和原理解析

1 案例

构建如下几个主要文件用于测试。

1
2
3
4
5
6
├── src
├── exports.js
├── module-exports.js
├── ES6-export.js
├── ES6-export-default.js
└── index.js

为便于比较,他们的结构大致相同:

exports.js

1
2
3
4
5
6
7
8
var printMsg = function printMsg() {
console.log("this message comes from exports");
};
exports.printMsg = printMsg;

console.log(exports);
console.log(module.exports);
console.log(exports === module.exports);

module-exports.js

1
2
3
4
5
6
7
8
var printMsg = function printMsg() {
console.log("this message comes from module-exports");
};
module.exports = printMsg;

console.log(exports);
console.log(module.exports);
console.log(exports === module.exports);

ES6-export.js

1
2
3
4
var printMsg = function printMsg() {
console.log("this message comes from ES6-export");
};
export { printMsg };

ES6-export-default.js

1
2
3
4
var printMsg = function printMsg() {
console.log("this message comes from ES6-export-default");
};
export default printMsg;

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from "react";
import ReactDOM from "react-dom";

import exports_test from "./exports";
import module_exports_test from "./module-exports";
import { printMsg } from "./ES6-export";
import any_name from "./ES6-export-default";

class App extends React.PureComponent {
componentDidMount() {
exports_test.printMsg();
module_exports_test();
printMsg();
any_name();
}

render() {
return null;
}
}

ReactDOM.render(<App />, document.getElementById("root"));

执行后,控制台输出结果如下:

1
2
3
4
5
6
7
8
9
10
Object { printMsg: printMsg() }
Object { printMsg: printMsg() }
true
Object { }
function printMsg()
false
this message comes from exports
this message comes from module-exports
this message comes from ES6-export
this message comes from ES6-export-default

CommonJS 是 NodeJs 服务器端模块的规范,ES Module 是在 ES5 中推出的,基于 CommonJS 规范,而 export 和 export default 是 ES6 中的模块化加载语法。在这之前,必须了解 ES6 模块与 CommonJS 模块完全不同。它们有两个重大差异:

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

第二个差异是因为 CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

2 ES5 中 exports 和 module.exports 的区别

  • module.exports 才是真正的接口,exports 是它的一个辅助工具。
  • nodejs 只会导出 module.exports 的指向,最终返回给调用者的是 module.exports,而不是 exports。
  • 所有的 exports 收集到的属性和方法,都赋值给了 module.exports。但前提是 module.exports 本身不具备任何属性和方法。如果 module.exports 已经具备一些属性和方法,那么从 exports 收集来的属性和方法将被忽略。

从输出结果来看,也印证了这一点,在 module-exports.js 中的 console.log(exports); 输出的对象没有属性;而 exports.js 的两个输出是具有相同属性的对象。

3 ES6 中 export 和 export default 区别

  • export 与 export default 均可用于导出常量、函数、文件、模块等。在一个文件或模块中 export、import 可以有多个,export default 仅有一个
  • export 方式只能具名导入,在导入时要加{ }
  • export default 则只能匿名导入
  • export 能直接导出变量表达式,export default 不行。