"import * as React from 'react'; 与 import React from 'react'; 的异同"
P粉596161915
2023-08-22 19:59:28
<p>我注意到<code>React</code>可以这样导入:</p>
<pre class="brush:js;toolbar:false;">import * as React from 'react';
</pre>
<p>...或者这样导入:</p>
<pre class="brush:js;toolbar:false;">import React from 'react';
</pre>
<hr />
<p>第一种方式导入了<code>react</code>模块的所有内容(参见:导入整个模块的内容)</p>
<p>第二种方式只导入了<code>default</code>模块导出(参见:导入默认导出)</p>
<hr />
<p>这两种方式似乎是不同且根本上不兼容的。</p>
<p>为什么它们都能工作?</p>
<hr />
<p>请参考源代码并解释机制...我对了解这是如何工作感兴趣。</p>
<hr />
<p><strong>更新</strong></p>
<p>这不是一个重复的问题,不同于“import * as react from 'react' vs import react from 'react'的区别”</p>
<p>那个问题是通过一般的ES6模块信息回答的。</p>
<p>我问的是使<code>react</code>模块能够这样工作的机制。它似乎与源代码中的“hacky”导出机制有关,但不清楚它如何使导入整个模块和只导入默认导出到<code>React</code>的两种方式都能与转译JSX等工作。</p>
你的
tsconfig.json
文件中很可能设置了"allowSyntheticDefaultImports": true,
,这实际上让编译器对它认为无效的默认导入保持静默。Typescript 添加了esModuleInterop
,它基本上与 babel 在模块加载方面做的事情相同。这使你能够在导入的源代码没有默认导出时使用 ES6 默认导入。
Typescript 在这方面是严格的(遵循规则),这就是为什么它要求你使用
import * as React from 'react'
。或者要求你在基本配置中告诉它允许合成默认导入。更多相关信息
TL;DR
实际上,ES的import语句
import default
和import *
并不是同一回事,它们在这种情况下表现相同是因为React作者选择以这种方式发布库并在TypeScript(使用esModuleInterop
)或Babel以及打包工具中使用兼容层使其“正常工作”。根据ES6规范,它可能不应该正常工作,但是今天我们仍然处于JS模块混乱的时代,因此像Babel、TypeScript、Webpack等工具都试图规范行为。更多细节:
React不是一个ES6库。如果你查看源代码,你会在
index.js
中看到这个:(注意注释,即使在React源代码中,他们也在努力解决ES6默认导出的兼容性问题。)
module.exports =
语法是CommonJS(NodeJS)的语法。浏览器无法理解这个语法。这就是为什么我们使用像Webpack、Rollup或Parcel这样的打包工具。它们理解各种模块语法,并生成应该在浏览器中工作的打包文件。但是,尽管React不是一个ES库,但是TypeScript和Babel都允许你像导入ES库一样导入它(使用
import
语法,而不是require()
等),但是CJS和ES之间存在一些需要解决的差异。其中之一是export =
可以为你提供ES没有规范兼容的导入方式,比如将函数或类作为模块导入。为了解决这些不兼容性问题,Babel允许你以默认方式导入CJS模块,或者以命名空间方式导入。TypeScript以前不支持这样做,但是最近在esModuleInterop
下添加了这个选项。因此,现在Babel和TypeScript都可以相当一致地允许使用默认或命名空间ES导入来导入CJS模块。对于TypeScript来说,还取决于库的类型定义是如何定义的。我不会详细介绍,但你可以想象在某些情况下,由于转译器和打包工具的原因,特定的导入在运行时可以正常工作,但TypeScript在编译时会出现错误。
另一个值得一提的事情是,如果你查看React的构建代码,还有一个UMD模块版本以及CJS版本。UMD版本包含一些复杂的运行时代码,以使其在任何模块环境中(包括浏览器)正常工作。它主要用于在运行时仅包含React(即不使用打包工具)的情况下使用。示例。
令人困惑吗?是的,我也这么认为。:)