首页  >  问答  >  正文

"import * as React from 'react'; 与 import React from 'react'; 的异同"

<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>
P粉596161915P粉596161915447 天前515

全部回复(2)我来回复

  • P粉486138196

    P粉4861381962023-08-23 10:44:14

    你的 tsconfig.json 文件中很可能设置了 "allowSyntheticDefaultImports": true,,这实际上让编译器对它认为无效的默认导入保持静默。Typescript 添加了 esModuleInterop,它基本上与 babel 在模块加载方面做的事情相同。

    这使你能够在导入的源代码没有默认导出时使用 ES6 默认导入。

    Typescript 在这方面是严格的(遵循规则),这就是为什么它要求你使用 import * as React from 'react'。或者要求你在基本配置中告诉它允许合成默认导入。

    更多相关信息

    回复
    0
  • P粉023650014

    P粉0236500142023-08-23 00:04:25

    TL;DR

    实际上,ES的import语句import defaultimport *并不是同一回事,它们在这种情况下表现相同是因为React作者选择以这种方式发布库并在TypeScript(使用esModuleInterop)或Babel以及打包工具中使用兼容层使其“正常工作”。根据ES6规范,它可能不应该正常工作,但是今天我们仍然处于JS模块混乱的时代,因此像Babel、TypeScript、Webpack等工具都试图规范行为。

    更多细节:

    React不是一个ES6库。如果你查看源代码,你会在index.js中看到这个:

    const React = require('./src/React');
    
    // TODO: decide on the top-level export form.
    // This is hacky but makes it work with both Rollup and Jest.
    module.exports = React.default || React;

    (注意注释,即使在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(即不使用打包工具)的情况下使用。示例

    令人困惑吗?是的,我也这么认为。:)

    回复
    0
  • 取消回复