import * as React from 'react'
import React from 'react'
这两个 import 语句理应有不同的含义,分别为:
- 引入
react的所有导出模块并定义别名为React - 引入
react导出的默认模块并定义别名为React
但在实际运用中,似乎两条语句总是可以达成相同的结果。可是为什么第一种写法可以实现第二种写法的功能呢?
React 的模块管理方式
首先我们要知道,react 为了代码可以运行在不同的平台上,会将 ES6 代码使用 Babel 转译为 commonjs 的模块标准,也就是说实际上它的导出方式是:
// react.js
function Components(){
}
function Fragment(){
}
module.exports = {
Components,
Fragment,
...
}
// 注意,react没有提供默认导出 module.exports.default = x
现在我们用 import React from 'react' 引入这个文件:
// main.js
import React from 'react'
console.log(React);
当时浏览器还只能运行支持 commonjs 的ES5,所以还要通过 Babel 进行转译:
// lib/main.js
"use strict";
var _react = _interopRequireDefault(require("./react"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
console.log(_react["default"]);
运行这个转译后的文件,可以得到打印结果:
{ Components: [Function: Components], Fragment: [Function: Fragment] }
现在把时间拨近,es-module 成为了浏览器原生支持的模块系统,import 语句不再需要编译也可以被浏览器所解析,此时我们在一个 typescript 项目中再次写下:
// main2.ts
import React from './react'
console.log(React);
我们期望它能正确运行,但错误却发生了:
模块“"/src/react"”没有默认导出。
编译器希望 /src/react 提供一个 module.exports.default 作为默认导出。但是显然 react 并没有提供这个默认模块。于是我们被迫要写上:
// main2.ts
import * as React from './react'
console.log(React);
即引入 react 的所有导出模块并定义别名为 React 。 这样才能正确导入 react 包。
新的疑问
但如果你在项目中使用 import React from 'react' 。你会发现它可以正确运行。我刚才说的报错是胡说八道? 当然不是。 这是因为 typescript 为了处理这种 commonjs 和 es-module 的兼容性问题而提供了一些配置上的解决方案:
/* tsconfig.json */
{
"compilerOptions": {
"allowJs": true,
// "allowSyntheticDefaultImports" : true ,
"esModuleInterop" : true,
}
}
allowJs允许 ts 项目中引入 js 文件allowSyntheticDefaultImports: 当一个模块没有默认导出时,允许import x from yesModuleInterop: 编译时添加额外的 js 代码以缓解对导入CommonJS模块的支持,启动它时会默认将allowSyntheticDefaultImports设置为true.
关于这些配置更加详细的信息可以参考:typescipt文档。
总结
- 使用
import * as React from 'react'是为了兼容 react 在分发包时使用的 commonjs 标准的模块导出语句 - typescript 提供了一些配置属性,在将 typesciprt 代码编译为 javascript 代码时处理了 commonjs 与 es-module 的兼容性问题。