平时在项目开发的过程中,因为 JavaScript 模块化的历史遗留问题,难免会存在在 CommonJS 的模块里头 import
ES6 的模块,或者在 CommonJS 的模块里头 require
ES6 的模块的情况发生。为了能够搞清楚他们之间的转换小秘密,所以就专门记录一下,方便日后回顾,顺便上来水一文。
因为这两种模块在网络上存在着各式各样的叫法,所以在文章开始前,为了能够和大家达成一致的共识。在后面我将会用 CJS 代指 CommonJS Module,用 ESM 代指 ES Modules。
ESM 转 CJS
1. ESM export 转 CJS exports
举个栗子:
1 2 3 4
| export var foo = "bar"; export function func() {} export default 1;
|
a.js
中的 ESM 在经过 babel 转为 CJS 后,代码变为:
注:在 ESM 被转为 CJS 时,转译器会在其导出的对象中定义一个值为 true
的私有的变量 __esModule
1 2 3 4 5 6 7
| "use strict"; Object.defineProperty(exports, "__esModule", { value: true });
exports.func = func; var foo = (exports.foo = "bar"); function func() {} exports.default = 1;
|
其实也就等价于:
1 2 3 4 5 6
| "use strict"; exports.__esModule = true;
exports.foo = "bar"; exports.func = function () {}; exports.default = 1;
|
2. ESM import 转 CJS require
2.1 Default import
还是一个栗子,直接默认导入 a.js
中的模块:
1 2 3 4
| import a from "./a";
console.log(a);
|
b.js
在经过 babel 转为 CJS 后,代码变为:
1 2 3 4 5 6 7 8 9 10
| "use strict"; Object.defineProperty(exports, "__esModule", { value: true });
var _a = require("./a"); var _a2 = _interopRequireDefault(_a); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } console.log(_a2.default);
|
2.2 Wildcard import
再一个栗子,名字空间导入 a.js
中的模块:
1 2 3 4
| import * as a from "./a";
console.log(a);
|
b.js
在经过 babel 转为 CJS 后,代码变为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| "use strict"; Object.defineProperty(exports, "__esModule", { value: true });
var _a = require("./a"); var A = _interopRequireWildcard(_a); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj;
return newObj; } }
|
3. 总结
- ESM 的
export
相当于往 CJS 的 exports
上添加属性
export var/let/const/function/class...
会往 exports
上添加同名属性
export default
会往 exports
上添加 default 属性
- ESM 可以 default
import
和 wildcard import
CJS
- default
import
CJS 时,会创建一个新的空对象,并把 CJS 的导出对象 exports
赋值到新对象的 default 属性
- wildcard
import
CJS 时,会创建一个新的空对象,并把 CJS 的导出对象 exports
中的自有属性浅复制到新的空对象中,最后再把导出对象 exports
赋值到新对象的 default 属性
ESM 中引用 CJS
根据上面得出的结论,ESM 中引用 CJS 有两种方式,栗子如下:
1 2 3 4 5
| module.export = { default: "myDefault", foo: "bar", };
|
1 2 3 4 5 6 7 8 9
| import cjs from "./cjs";
import * as cjs2 from "./cjs";
import { default as cjs3 } from "./cjs";
|
CJS 中引用 ESM
在 CJS 中引用 ESM 相当于直接引用 ESM 转成 CJS 的 module.exports
,栗子如下:
1 2 3 4 5 6
| export let foo = "bar"; export { foo as bar }; export function func() {} export class cls {} export default 1;
|
1 2 3 4 5 6 7 8 9 10 11
| var es = require("./es");
|