前言
前端开发时基本都有babel了,可以 import moment from 'moment';,运行正常。
但是在nodejs的typescript下,有时会报错没有默认导出,要使用import * as moment from 'moment'; 。
此时请检查 tsconfig.json 的 esModuleInterop 选项,开启该选项将影响实际编译 并解决此问题 (参考下面babel的处理方式)。另外 allowSyntheticDefaultImports 默认取 esModuleInterop 的值,该选项只影响typescript提示,不影响编译。
因为前端Webpack能够处理commonjs模块,所以下面都编译成commonjs、ES5,且typescript不启用 esModuleInterop 选项
typescript
typescript处理ES6 export
index.ts:
export default 1;
export const a =2;
编译成:
"use strict";
exports.__esModule = true;
exports["default"] = 1;
exports.a = 2;
就是编译成commonjs规范,直接往exports对象挂属性就行了
exports.__esModule 是普遍规范要求的,这里暂时没啥用
typescript处理ES6 import
index2.ts:
import example, { a } from "./index";
console.log(example, a);
编译成
"use strict";
exports.__esModule = true;
var index_1 = require("./index");
console.log(index_1["default"], index_1.a);
index_1 是commonjs规范加载回来的对象,这个就是 index.js 文件里面的 exports 对象。
我们在index2.ts的 console.log 中使用的 example 和 a 变量,编译后会用 index_1["default"] 和 index_1.a 进行替换
babel
babel处理ES6 export
index.js:
export default 1;
编译成:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _default = 1;
exports["default"] = _default;
具体代码与ts不同,但是采用的是同一个套路。
babel处理ES6 import
index2.js:
import example from "./index";
console.log(example)
编译成:
"use strict";
var _index = _interopRequireDefault(require("./index"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
console.log(_index["default"]);
一样的套路,index2.js console.log 的 example 变量,变成 _index["default"] 。
babel会有一个“黑魔法”,如果加载的文件是esModule规范(从__esModule属性得知),那么无需处理,_index变量就是require得到的对象。
否则,得到的_index 是{"default": require("./index")} ,也就是babel把默认导出的对象包了一层放到新对象的default属性去了。
总结
-
这里不严谨的总结一下,
import的基本原理就是import 'xxx' === require('xxx').default -
babel导入moment.js等commonjs模块时,把他们包装了一下放到新对象的default属性去。而ts不启用
esModuleInterop选项时,没有此特殊操作。