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

IT技术 javascript node.js ecmascript-6 babeljs
2021-01-09 18:01:01

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

  1. 导入使用的modulerequire,并使用导出module.exportsexports.foo
  2. 使用 ES6 导入module,使用 ES6import导出export

使用其中一种是否有任何性能优势?如果我们要使用 ES6 module而不是 Node module,还有什么我们应该知道的吗?

6个回答
更新

自 Node v12(2019 年 4 月)以来,默认启用对 ES module的支持,自 Node v15(2020 年 10 月)以来它是稳定的(请参阅此处)。包含节点module.mjspackage.json文件必须以"type": "module". 节点的文件有一吨的详细信息,还约CommonJS的和ESmodule之间的互操作。

在性能方面,新功能总是有可能不如现有功能优化得那么好。然而,由于module文件只评估一次,性能方面可能会被忽略。最后,无论如何,您都必须运行基准测试才能获得明确的答案。

ES module可以通过该import()函数动态加载与 不同require,这会返回一个Promise。


上一个答案

使用其中一种是否有任何性能优势?

请记住,目前还没有原生支持 ES6 module的 JavaScript 引擎。你自己说你正在使用 Babel。无论如何,Babel 都会默认转换importexport声明为 CommonJS ( require/ module.exports)。因此,即使您使用 ES6 module语法,如果您在 Node.js 中运行代码,您也会在幕后使用 CommonJS。

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

由于 ES6 module是标准的一部分,我会使用它们。

ES6 module在最新的 V8 中,并且也在其他浏览器中加入了标志。请参阅:medium.com/dev-channel/...
2021-03-11 18:01:01
@Entei:似乎您想要默认导出,而不是命名导出。module.exports = ...;相当于export default ...exports.foo = ...相当于export var foo = ...
2021-03-23 18:01:01
除非您需要进行动态导入
2021-03-23 18:01:01
我尝试使用ES6 importwithrequire但它们的工作方式不同。CommonJS 导出类本身,而只有一个类。ES6 导出就像有多个类一样,所以你必须使用.ClassName来获取导出的类。是否还有其他实际影响实施的差异
2021-04-06 18:01:01
值得注意的是,即使 Babel 最终import在 Node中转译为CommonJS,与 Webpack 2 / Rollup(以及任何其他允许 ES6 树抖动的打包器)一起使用,但最终得到的文件可能比 Node 的等效代码要小得多通过使用require正是因为ES6 允许对导入/导出进行静态分析。虽然这不会对 Node 产生影响(目前),但如果代码最终将作为单个浏览器包结束,它当然可以。
2021-04-08 18:01:01

您可能需要考虑多种用途/功能:

要求:

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

ES6 导入:

  • 您可以使用命名导入来选择性地仅加载您需要的部分。这样可以节省内存。
  • 导入可以是异步的(在当前的 ES6 module加载器中,它实际上是)并且可以执行得更好一点。

此外,Require module系统不是基于标准的。既然存在 ES6 module,它就不太可能成为标准。未来将在各种实现中原生支持 ES6 module,这将在性能方面具有优势。

你提到了一些非常重要的东西:module加载器。虽然 ES6 提供了导入和导出语法,但它没有定义module应该如何加载。重要的部分是声明是可静态分析的,因此可以在不执行代码的情况下确定依赖关系。这将允许module加载器同步或异步加载module。但是 ES6 module本身不是同步或异步的。
2021-03-17 18:01:01
@FelixKling ES6 module加载器在 OP 中被标记,所以我认为它与答案相关。我还表示,基于观察 async 是当前行为,以及未来(在任何实现中)的可能性,因此这是一个需要考虑的相关点。你认为这是错误的吗?
2021-03-19 18:01:01
是什么让您认为 ES6 导入是异步的?
2021-03-25 18:01:01
我认为不要将module系统/语法与module加载器混为一谈很重要。例如,如果你为 node 开发,那么你很可能会编译 ES6 modulerequire,所以无论如何你都在使用 Node 的module系统和加载器。
2021-03-25 18:01:01
@FelixKling - 各种观察的组合。使用 JSPM(ES6 module加载器...)我注意到,当导入修改了全局命名空间时,在其他导入中没有观察到效果(因为它们是异步发生的。这也可以在转译的代码中看到)。此外,由于这是行为(1 次导入不会影响其他人),因此没有理由不这样做,因此它可能取决于实现
2021-04-05 18:01:01

就目前 ES6 导入而言,导出始终编​​译为 CommonJS,因此使用其中之一没有任何好处虽然推荐使用 ES6,因为当浏览器的本机支持发布时它应该是有利的。原因是,您可以从一个文件导入部分文件,而使用 CommonJS,您必须需要所有文件。

ES6 → import, export default, export

CommonJS → require, module.exports, exports.foo

以下是这些的常见用法。

ES6 导出默认

// hello.js
function hello() {
  return 'hello'
}
export default hello

// app.js
import hello from './hello'
hello() // returns hello

ES6导出多个导入多个

// hello.js
function hello1() {
  return 'hello1'
}
function hello2() {
  return 'hello2'
}
export { hello1, hello2 }

// app.js
import { hello1, hello2 } from './hello'
hello1()  // returns hello1
hello2()  // returns hello2

CommonJS module.exports

// hello.js
function hello() {
  return 'hello'
}
module.exports = hello

// app.js
const hello = require('./hello')
hello()   // returns hello

CommonJS module.exports 多个

// hello.js
function hello1() {
  return 'hello1'
}
function hello2() {
  return 'hello2'
}
module.exports = {
  hello1,
  hello2
}

// app.js
const hello = require('./hello')
hello.hello1()   // returns hello1
hello.hello2()   // returns hello2
您实际上也可以Object Destructuring在使用 CommonJS require 时使用。所以你可以有:const { hello1, hello2 } = require("./hello");有点类似于使用导入/导出。
2021-03-27 18:01:01
这是迄今为止最好的答案,因为它不仅提供了描述,还提供了实际的代码片段。
2021-04-07 18:01:01

主要优点是语法:

  • 更具声明性/紧凑的语法
  • ES6 module基本上会使 UMD(通用module定义)过时——基本上消除了 CommonJS 和 AMD(服务器与浏览器)之间的分歧。

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

即使浏览器具有完整的 ES6 module支持,您能否澄清为什么还需要捆绑器?
2021-03-18 18:01:01
正如@snozza 所说......“导入/导出module功能并未天真地在任何浏览器中实现。仍然需要转译器”
2021-03-22 18:01:01
您不再需要任何额外的库。自 v8.5.0(一年多前发布)以来,node --experimemntal-modules index.mjs让您import无需 Babel 即可使用您还可以(并且应该)将您的 npm 包发布为原生 ESModule,并具有对旧require方式的向后兼容性许多浏览器本身也支持动态导入
2021-03-22 18:01:01
道歉,编辑更有意义。我的意思是导入/导出module功能未在任何浏览器中实现。仍然需要转译器。
2021-03-30 18:01:01
对我来说,这似乎有点矛盾。如果有完全支持,那么打包程序的目的是什么?ES6 规范中是否缺少某些内容?完全受支持的环境中,捆绑器实际上会做什么?
2021-04-04 18:01:01

使用其中一种是否有任何性能优势?

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

一些对比图http://kangax.github.io/compat-table/es6/没有考虑到这一点,所以当你看到Chrome几乎所有的绿色时,小心点。importES6 中的关键字尚未考虑在内。

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

(在V8 根据 ES6 规范实现它之前,我们可能还有一些错误或几年之后。)

这份文件是我们需要的,这份文件是我们必须遵守的。

ES6 标准说在我们读取module之前应该有module依赖项,就像在编程语言 C 中一样,我们有(头文件).h文件。

这是一个良好且经过充分测试的结构,我相信创建 ES6 标准的专家考虑到了这一点。

这使得 Webpack 或其他包打包器能够在某些特殊情况下优化包,并减少包中不需要的一些依赖项。但是如果我们有完美的依赖关系,这永远不会发生。

import/export原生支持上线还需要一些时间,而且require关键字在很长一段时间内都不会去任何地方。

什么是require

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

Node 使用系统级方法来读取文件。使用require. require将在一些系统调用uv_fs_open(取决于最终系统、Linux、Mac、Windows)中结束以加载 JavaScript 文件/module。

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

在此处输入图片说明

您的回答完全正确,但我认为我们正在比较两个不同的特征。全部 import/export转换为require,授予。但是这一步之前发生的事情可以被认为是“性能”增强。示例:如果lodash是用 ES6 编写的并且你import { omit } from lodash,最终的 bundle 将只包含 'omit' 而不是其他实用程序,而 simplerequire('lodash')将导入所有内容。这将增加包的大小,下载时间更长,因此会降低性能。当然,这仅在浏览器上下文中有效。
2021-03-15 18:01:01
我更新了@LeeBenson 的答案,我想如果我们考虑浏览器引擎的本机支持,我们还无法进行比较。使用 Webpack 提供的方便的三个抖动选项,甚至在我们设置 CommonJS module之前也可以实现,因为对于大多数实际应用程序我们知道应该使用哪些module。
2021-03-19 18:01:01
您正在俯瞰“摇树”。您的要点链接中没有任何地方讨论过摇树。使用 ES6 module启用它,因为importexport是导入特定代码路径的静态声明,而require可以是动态的,因此捆绑在未使用的代码中。性能优势是间接的——Webpack 2 和/或 Rollup 可能导致包大小更小,下载速度更快,因此对(浏览器的)最终用户来说看起来更快捷。这只适用于所有代码都是用 ES6 module编写的,因此可以静态分析导入。
2021-03-22 18:01:01
其实,还有一个领域,性能可以得到改善-集束大小。使用import在一个的WebPack 2 /汇总构建过程可以潜在地减少通过生成的文件大小“树摇动”未使用的module/代码,否则可能在最终束卷起。较小的文件大小 = 下载速度更快 = 在客户端上初始化/执行速度更快。
2021-03-24 18:01:01
原因是地球上当前的浏览器都不允许import 本地使用关键字。或者这意味着您无法从 JavaScript 文件导入另一个 JavaScript 文件。这就是您无法比较这两者的性能优势的原因。但是当然,像 Webpack1/2 或 Browserify 这样的工具可以处理压缩。他们并驾齐驱:gist.github.com/substack/68f8d502be42d5cd4942
2021-03-29 18:01:01