import React from 'react' 和 import * as React from 'react' 的区别
创建一段测试代码:
import React from 'react';
console.log(React)
使用ts默认编译配置(ts在默认会将代码编译为es5):
"use strict";
exports.__esModule = true;
var react_1 = require("react");
console.log(react_1["default"]);
再看一下
import * as React from 'react'
console.log(React.useEffect)
"use strict";
exports.__esModule = true;
var react_1 = require("react");
console.log(react_1);
在默认情况下ts会将esm模块编译成commonjs
- 对于 export default 的变量,TS 会将其放在 module.exports 的 default 属性上
- 对于 export 的变量,TS 会将其放在 module.exports 对应变量名的属性上
- 额外给 module.exports 增加一个 __esModule: true 的属性,用来告诉编译器,这本来是一个 esm 模块
这个时候我们再看一下npm包中react的导出
可以看到通过npm方式引用react时默认是以commonjs方式导出的,结合上面ts默认编译的规则,import React from 'react' 会从 exports.default 上去拿代码,显然此时default属性不存在commonjs模块中,因此会导致打印undefined;而import * as React from 'react' 则会把React作为为一个对象因此不会有问题。
esModuleInterop 和 allowSyntheticDefaultImports
上面的问题延伸一下,其实不仅仅是引入react,在esm中引入任何commonjs的模块在ts默认编译时都会有这样的问题,ts提供了esModuleInterop 和 allowSyntheticDefaultImports 这两个配置来影响ts默认的解析。
allowSyntheticDefaultImports 是一个类型检查的配置,它会把 import 没有 exports.default 的报错忽略,如果你的target是es6加上这个配置就够了,但如果你的目标代码是es5仅仅加上这个还不行,还需要使用esModuleInterop,因为它才会改变tsc的编译产物:
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"esModuleInterop":true
}
}
// index.ts
import React from 'react';
console.log(React.useEffect)
// tsc产物
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importDefault(require("react"));
console.log(react_1.default.useEffect);
在加上esModuleInterop 之后编译产物多了一个_importDefault 辅助函数,而他的作用就是给module.exports 加上default 属性。
根据 ts官网的说明
开启esModuleInterop的同时也会默认开启allowSyntheticDefaultImports,因此更推荐直接加esModuleInterop。
扩展
除了使用ts来编译ts,还可以使用babel来处理ts文件,扩展阅读2而babel处理import时采用的方式和ts的esModuleInterop类似:
使用babel-cli(需要 Babel 7)执行: npx babel ./ --out-dir ./ --extensions ".ts" --presets babel-preset-es2015
'use strict';
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
console.log(_react2.default);
其中_interopRequireDefault辅助函数思路和ts是一致的。
总结
1.使用 import * as React from 'react' 引入
2.使用 import React from 'react' 配合 tsconfig esModuleInterop:true [推荐]