【问题标题】:import * as React from 'react'; vs import React from 'react';从'react'导入*作为React; vs 从 'react' 导入 React;
【发布时间】:2019-08-12 14:54:27
【问题描述】:

我注意到React 可以这样导入:

import * as React from 'react';

...或者像这样:

import React from 'react';

第一个导入 react 模块中的所有内容(参见:Import an entire module's contents

第二个只导入default 模块导出(参见:Importing defaults


这两种方法似乎是不同的,并且从根本上不兼容。

为什么它们都有效?


请参考源代码并解释机制...我有兴趣了解它是如何工作的。


更新

不是What is the difference between import * as react from 'react' vs import react from 'react' 的副本

这个问题是用一般的 ES6 模块信息回答的。

我在询问使react 模块像这样工作的机制。它似乎与“hacky”导出机制in the source here 相关,但尚不清楚如何同时导入 整个 模块和 default 导出到 React 和让这两种方法都适用于转译 JSX 等。

【问题讨论】:

  • 你问Why do they both work? 我问你Why wouldn't they work?
  • 如果只导入默认值和所有内容,给您相同的结果,您认为“所有内容”意味着什么?您是否查看过您要导入的来源?
  • import * as React from 'react'; 包括default,我想。
  • 在打字稿中,您可以在tsconfig.jsonallowSyntheticDefaultImports 中指定。这就是让您import React from 'react' 的原因。 Javascript/babel 在这里“作弊”,让您在实际不存在时进行合成默认导入。 React 导出方式的正确语法应该是 import * as React from 'react'
  • @KevinB 我查看了源代码,似乎与this "hacky" line 有关,但我不清楚它是如何工作的

标签: javascript reactjs typescript react-native ecmascript-6


【解决方案1】:

您很可能在您的tsconfig.json 中设置了"allowSyntheticDefaultImports": true,,这实际上使编译器关闭了它认为无效的默认导入。 Typescript 添加了 esModuleInterop,它基本上完成了 babel 加载模块的工作。

这允许你使用 ES6 默认导入,即使你导入的源代码没有默认导出任何东西

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

More On That Here

【讨论】:

  • 有趣,这可以解释 TypeScript,但它也适用于 JavaScript,所以肯定有其他事情发生。你在上面的评论中提到了Babel……JSX 的Babel 转译器会不会做一些棘手的事情?
  • @brian-lives-outdoors 是的,babel 本质上是默认这样做的。 I believe its here。还有this is a pretty good read
  • 实际上是esModuleInterop 使这项工作有效。 Babel 在处理模块时做了类似的事情。模块有点乱,根据 ES6 规范,import defaultimport * 不应该是同一个东西,但通常它们在编译器和捆绑器中是可以互换的,以避免混淆或不兼容。
  • Great article约翰,谢谢。在我接受之前,我会阅读此内容,但看起来你和@Aaron 是对的……是编译器让它工作
  • @Aaron 是的,我提到过。 allowSyntheticDefaultImportsesModuleInterop 之前的解决方案
【解决方案2】:

TL;DR

确实 ES 导入语句 import defaultimport * 不是一回事,在这种情况下它们的行为相同的事实是 React 作者选择如何在 TypeScript 中发布库和兼容性层的组合(使用 @ 987654326@) 或 Babel 和你的打包工具让它们“正常工作”。根据 ES6 规范,它可能不应该工作,但是今天我们仍然在一个 JS 模块一团糟的时代工作,所以像 Babel、TypeScript、Webpack 等工具试图规范行为。

更多细节:

React 不是 ES6 库。如果您查看the source code,您会在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 都允许你像它一样导入它(使用 import 语法,而不是 require() 等),但 CJS 和 ES 之间存在差异必须解决。其中之一是export = 可以 为您提供 ES 没有符合规范的导入方式,例如作为模块的函数或类。为了解决这些不兼容问题,Babel 有一段时间允许您导入 CJS 模块,就好像它们默认导出某些东西一样,作为命名空间导入。 TypeScript 有一段时间没有这样做,但最近在esModuleInterop 下将其添加为一个选项。所以现在 Babel 和 TypeScript 都可以非常一致地允许使用默认或命名空间 ES 导入来导入 CJS 模块。

对于 TypeScript,它还取决于库的类型定义是如何实际定义的。我不会深入讨论,但您可以想象由于转译器和捆绑器,特定导入在运行时工作,但 TypeScript 编译时不会出现错误的情况。

另一件值得一提的事情是,如果您查看 React 的构建代码,则有一个 UMD module 版本以及 CJS 版本。 UMD 版本包含一些粗糙的运行时代码,试图使其在任何模块环境中工作,包括浏览器。它主要用于如果您只想在运行时包含 React(即您不使用捆绑器)。 Example.

令人困惑?是的,我想是的。 :)

【讨论】:

  • 此处引用的代码段已不在 index.js 文件中。您可以在这里@Aaron 给我们更新吗?
猜你喜欢
  • 2020-12-29
  • 2021-11-01
  • 2018-07-03
  • 2019-06-17
  • 1970-01-01
  • 1970-01-01
  • 2019-10-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多