使用 Node.js require 与 ES6 导入 / 导出

在我正在合作的项目中,关于可以使用哪种模块系统,我们有两个选择:

  1. 导入使用模块require ,并利用出口module.exportsexports.foo
  2. 使用 ES6 import导入模块,并使用 ES6 export

相互使用是否对性能有好处?如果要在 Node 模块上使用 ES6 模块,还有其他什么应该知道的吗?

答案

相互使用是否对性能有好处?

请记住,还没有 JavaScript 引擎本身支持 ES6 模块。您说自己正在使用 Babel。无论如何, module.exports默认情况下module.exportsimportexport声明转换为 CommonJS( require / module.exports )。因此,即使您使用 ES6 模块语法,但如果在 Node 中运行代码,也将在后台使用 CommonJS。

CommonJS 和 ES6 模块之间存在技术差异,例如,CommonJS 允许您动态加载模块。 ES6 不允许这样做, 但是正在为此开发一个 API

由于 ES6 模块是标准的一部分,因此我将使用它们。

您可能要考虑几种用法 / 功能:

要求:

  • 您可以在未预定义的模块名称 / static 的情况下进行动态加载,或者仅在 “确实需要” 模块时才有条件地加载模块(取决于某些代码流)。
  • 加载是同步的。这意味着,如果您有多个require ,它们将被一个接一个地加载和处理。

ES6 导入:

  • 您可以使用命名的导入来有选择地仅加载所需的片段。这样可以节省内存。
  • 导入可以是异步的(实际上是在当前的 ES6 Module Loader 中),并且可以执行得更好。

另外,Require 模块系统也不基于标准。由于存在 ES6 模块,因此极不可能成为标准。将来,将在各种实现中对 ES6 模块提供本地支持,这在性能方面将是有利的。

主要优点是语法:

  • 更多说明性 / 紧凑语法
  • ES6 模块将基本上使 UMD(通用模块定义)过时 - 实质上消除了 CommonJS 和 AMD(服务器与浏览器)之间的分裂。

您不太可能看到 ES6 模块的任何性能优势。即使浏览器完全支持 ES6 功能,您仍将需要一个额外的库来捆绑模块。

相互使用是否对性能有好处?

当前答案是否定的,因为当前没有浏览器引擎实现从 ES6 标准的import/export

一些比较表http://kangax.github.io/compat-table/es6/没有考虑到这一点,因此当您看到 Chrome 的几乎所有绿色时,请小心。未考虑来自 ES6 的import关键字。

换句话说,包括 V8 在内的当前浏览器引擎无法通过任何 JavaScript 指令从主 JavaScript 文件导入新的 JavaScript 文件

(直到 V8 根据 ES6 规范实现这一目标为止,我们距离我们可能只有几步之遥,也可能只有年之遥。)

这个文件是我们需要的,而这个文件是我们必须服从。

ES6 标准表示,在像使用编程语言 C 那样拥有(头) .h文件的方式读取模块之前,应该存在模块依赖性。

这是一个经过良好测试的良好结构,并且我相信创建 ES6 标准的专家会牢记这一点。

这使 Webpack 或其他程序包捆绑器可以在某些特殊情况下优化该程序包,并减少一些不必要的依赖关系。但是,如果我们有完美的依赖关系,这将永远不会发生。

import/export本机支持生效之前,将需要一些时间,并且require关键字很长时间不会出现。

有什么require

这是node.js加载模块的方式。 ( https://github.com/nodejs/node

Node 使用系统级方法来读取文件。在使用require时,基本上可以依靠它。 require将以uv_fs_open类的系统调用uv_fs_open (取决于最终系统,Linux,Mac,Windows)以加载 JavaScript 文件 / 模块。

要检查是否正确,请尝试使用 Babel.js,您将看到import关键字将转换为require

在此处输入图片说明

使用 ES6 模块对于 “摇树” 很有用;例如,启用 Webpack 2,汇总(或其他捆绑程序)以标识未使用 / 导入的代码路径,因此不要将其放入结果捆绑包中。通过消除不需要的代码,可以大大减少文件的大小,但是 CommonJS 是默认捆绑的,因为 Webpack 等无法知道是否需要它。

这是通过对代码路径进行静态分析来完成的。

例如,使用:

import { somePart } 'of/a/package';

…… 为捆绑程序提供了一个提示,即不需要package.anotherPart (如果未导入,则不能使用它 - 对吗?),因此不会打扰它。

要为 Webpack 2 启用此功能,您需要确保您的编译器不会吐出 CommonJS 模块。如果您将es2015插件与 babel 一起使用,则可以在.babelrc其禁用, .babelrc所示:

{
  "presets": [
    ["es2015", { modules: false }],
  ]
}

汇总和其他工具的工作方式可能有所不同 - 如果您有兴趣,请查看文档。

当涉及到异步或延迟加载时,那么import ()功能要强大得多。看看何时需要异步方式的组件,然后以异步方式使用import ,就像使用await const变量一样。

const module = await import('./module.js');

或者,如果您想使用require()

const converter = require('./converter');

实际上, import()实际上是异步的。正如 neehar neehar venugopal 在ReactConf 中提到的那样,您可以使用它为客户端架构动态加载React组件。

在路由方面也更好。这是使用户在连接到特定网站的特定组件时网络日志下载必要部分的一件事。例如,在仪表板无法下载仪表板的所有组件之前的登录页面。因为当前需要什么,即登录组件,所以仅将其下载。

这同样适用于export :ES6 export是为 CommonJS 的完全相同module.exports

- 如果正在开发 node.js 项目,则必须严格使用require()因为如果使用import ,节点将抛出异常错误,因为invalid token 'import' 。因此,节点不支持导入语句。

更新 - 正如Dan Dascalescu所建议:自 v8.5.0(2017 年 9 月发布)以来, node --experimental-modules index.mjs允许您在不使用 Babel 的情况下使用import 。您还可以(并且应该) 将 npm 软件包发布为本地 ESModule,并与旧的require方法具有向后兼容性

有关在哪里使用异步导入的更多信息,请参见此 - https: //www.youtube.com/watch?v=bb6RCrDaxhw

最重要的是,ES6 模块确实是一个官方标准,而 CommonJS(Node.js)模块则不是。

在 2019 年, 84%的浏览器都支持 ES6 模块。尽管 Node.js 将它们放在--experimental-modules标志的后面,但还有一个方便的节点程序包esm ,它使集成更加流畅。

您可能会在这些模块系统之间遇到的另一个问题是代码位置。 Node.js 假定源代码保存在node_modules目录中,而大多数 ES6 模块则部署在平面目录结构中。这些都不容易调和,但是可以通过使用安装前和安装package.json本来入侵您的package.json文件来完成。下面是一个例子同构模块文章解释它是如何工作的。

我个人使用 import 是因为,我们可以使用 import 导入所需的方法,成员。

import {foo, bar} from "dep";

文件名: dep.js

export foo function(){};
export const bar = 22

归功于 Paul Shan。 更多信息

不知道为什么(可能是优化 - 延迟加载?)是否可以那样工作,但是我注意到如果不使用导入的模块, import可能不会解析代码。
在某些情况下,这可能不是预期的行为。

以讨厌的 Foo 类作为我们的示例依赖项。

foo.ts

export default class Foo {}
console.log('Foo loaded');

例如:

索引

import Foo from './foo'
// prints nothing

索引

const Foo = require('./foo').default;
// prints "Foo loaded"

索引

(async () => {
    const FooPack = await import('./foo');
    // prints "Foo loaded"
})();

另一方面:

索引

import Foo from './foo'
typeof Foo; // any use case
// prints "Foo loaded"