使用Node.js需要与ES6导入/导出

kpi*_*mov 840 javascript node.js ecmascript-6 babeljs

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

  1. 使用require和导出模块,并使用module.exports和导出exports.foo.
  2. 使用ES6导入模块import,并使用ES6导出export

使用一个优于另一个是否有任何性能优势?如果我们使用ES6模块而不是Node模块,还有什么我们应该知道的吗?

Fel*_*ing 667

使用一个优于另一个是否有任何性能优势?

请记住,还没有本身支持ES6模块的JavaScript引擎.你说你自己在使用巴别塔.无论如何,Babel 默认转换importexport声明为CommonJS(require/ module.exports).因此,即使您使用ES6模块语法,如果您在Node中运行代码,也将使用CommonJS.

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

由于ES6模块是标准的一部分,我会使用它们.

  • @Entei:好像你想要一个默认导出,而不是命名导出.`module.exports = ...;`相当于`export default ...`.`exports.foo = ...`相当于`export var foo = ...`; (71认同)
  • 我尝试用`require`来使用`ES6 import`,但它们的工作方式不同.CommonJS只导出类本身,而只有一个类.ES6导出就像有多个类一样,所以你必须使用`.ClassName`来获取导出的类.是否存在实际影响实施的其他差异 (15认同)
  • 值得注意的是,尽管Babel最终将"导入"转换为Node中的CommonJS,与Webpack 2/Rollup(以及允许ES6树抖动的任何其他捆绑器)一起使用,但最终可能会出现一个明显小于等效文件的文件.代码节点通过使用`require`完全_abausech,因为ES6允许静态分析导入/导出.虽然这对Node(但是)没有任何影响,但如果代码最终将作为单个浏览器捆绑包结束,它当然可以. (10认同)
  • 除非您需要进行动态导入 (4认同)
  • ES6模块位于最新的V8中,并且还带有其他标志。参见:https://medium.com/dev-channel/es6-modules-in-chrome-canary-m60-ba588dfb8ab7 (3认同)
  • @stackjlei何时嵌入到网站中?多亏了诸如webpack之类的模块捆绑器,使用npm实际上并不意味着代码是在节点中执行的。 (3认同)
  • @LeeBenson实际上很多代码都不是这样.例如,请参阅[rollup/lodash问题](https://github.com/rollup/rollup/issues/610). (2认同)
  • 从v8.5.0(一年多以前发布)开始,`node --experimemntal-modules index.mjs`允许您不带Babel使用`import`。您还可以(并且应该)[将npm软件包发布为具有向后兼容性的本机ESModule](https://medium.com/@dandv/publishing-native-es-modules-with-node-v8-5-0- 730736e0f612)。 (2认同)
  • @Akhila:添加了更新。如果您认为这就足够了或者我是否应该添加更多内容,请告诉我。感谢您就此事联系我。 (2认同)

Ami*_*mit 166

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

要求:

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

ES6进口:

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

此外,Require模块系统不是基于标准的.现在ES6模块存在的可能性很小.将来,在各种实现中将对ES6模块提供原生支持,这在性能方面将是有利的.

  • 你提到了非常重要的东西:模块加载器.虽然ES6提供了导入和导出语法,但它没有定义应该如何加载模块.重要的是声明是静态可分析的,因此可以在不执行代码的情况下确定依赖关系.这将允许模块加载器同步或异步加载模块.但ES6模块本身不是同步或异步的. (33认同)
  • 是什么让你认为ES6导入是异步的? (16认同)
  • 我认为重要的是不要将模块系统/语法与模块加载器混淆.例如,如果您为节点开发,那么您可能无论如何都要将ES6模块编译为"require",因此无论如何您都在使用Node的模块系统和加载器. (9认同)
  • @FelixKling - 各种观察的组合.使用JSPM(ES6 Module Loader ...)我注意到当导入修改了全局命名空间时,在其他导入中没有观察到这种效果(因为它们是异步发生的..这也可以在转换后的代码中看到).此外,由于这是行为(1导入不影响其他行为),因此没有理由不这样做,因此它可能依赖于实现 (5认同)
  • @FelixKling ES6模块加载器在OP中被标记,因此我认为它与答案相关.我还说基于观察异步是当前的行为,以及将来(在任何实现中)的可能性,因此这是一个需要考虑的相关点.你觉得这是错的吗? (4认同)

Has*_*alp 53

截至目前 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

CommonJS 模块.exports

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

// app.js
const hello = require('./hello')
hello()   // returns hello
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

  • 这是迄今为止最好的答案,因为它不仅提供了描述,还提供了实际的代码片段。 (12认同)
  • 实际上,在使用 CommonJS require 时,您也可以使用“对象解构”。所以你可以有:`const { hello1, hello2 } = require("./hello");`,它将**有点**类似于使用导入/导出。 (9认同)

sno*_*zza 40

主要优点是句法:

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

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

  • 这对我来说似乎有些矛盾.如果有**完全支持**那么捆绑器的目的是什么?ES6规格中是否缺少某些内容?在完全支持的**环境中,捆绑商实际上做了什么? (16认同)
  • 您是否可以澄清为什么即使浏览器具有完整的ES6模块支持,也需要捆绑器? (4认同)
  • 您不再需要任何额外的库.从v8.5.0(一年多前发布)开始,`node --experimemntal-modules index.mjs`允许你使用不带Babel的`import`.您可以(也应该)[将您的npm软件包发布为本机ESModule,具有向后兼容性](https://medium.com/@dandv/publishing-native-es-modules-with-node-v8-5-0- 730736e0f612)旧的`require`方式.许多浏览器本身也支持[动态导入](https://developers.google.com/web/updates/2017/11/dynamic-import). (2认同)

pro*_*sti 35

使用一个优于另一个是否有任何性能优势?

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

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

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

(根据ES6规范,在V8实现之前,我们可能仍然只是几个或几年之后.)

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

ES6标准表示模块依赖性应该在我们读取模块之前就像在编程语言C中那样,我们有(头文件).h文件.

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

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

import/export原生支持上线之前需要一些时间,require关键字不会长时间在任何地方.

什么是require

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

Node使用系统级方法来读取文件.你在使用时基本上都依赖它require.require将结束一些系统调用uv_fs_open(取决于终端系统,Linux,Mac,Windows)加载JavaScript文件/模块.

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

在此输入图像描述

  • 你忽略了'树摇晃'.在你的要点链接中没有任何地方讨论树摇晃.使用ES6模块启用它,因为`import`和`export`是导入特定代码路径的静态声明,而`require`可以是动态的,因此捆绑在未使用的代码中.的性能优势是indirect--的WebPack 2和/或汇总可以在更小的束大小是更快的下载_potentially_结果,并且因此出现迅捷给最终用户(的浏览器).这仅适用于所有代码都是用ES6模块编写的,因此可以静态分析导入. (4认同)
  • 实际上,有一个领域可以改进性能* - 捆绑尺寸.在Webpack 2/Rollup构建过程中使用`import`可以通过'树摇动'未使用的模块/代码来减少由此产生的文件大小,否则可能会在最终捆绑中结束.较小的文件大小=下载速度更快=在客户端上更快地执行/执行. (2认同)
  • 原因是地球上没有当前的浏览器允许本地使用`import`关键字.或者这意味着您无法从JavaScript文件导入另一个JavaScript文件.这就是为什么你无法比较这两者的性能优势.但是,当然,像Webpack1/2或Browserify这样的工具可以处理压缩.他们是一对一的:https://gist.github.com/substack/68f8d502be42d5cd4942 (2认同)
  • 我更新了答案@LeeBenson,我认为,如果考虑到浏览器引擎的本机支持,我们还无法比较。即使在我们设置CommonJS模块之前,也可以使用Webpack来实现方便的三摇选项,因为对于大多数实际应用程序,我们知道应该使用哪些模块。 (2认同)

Lee*_*son 30

使用ES6模块可以用于"树摇动"; 即启用Webpack 2,Rollup(或其他捆绑包)来识别未使用/导入的代码路径,因此不会将其放入生成的捆绑包中.这可以通过消除您永远不需要的代码来显着减少其文件大小,但默认情况下捆绑了CommonJS,因为Webpack等人无法知道是否需要它.

这是使用代码路径的静态分析完成的.

例如,使用:

import { somePart } 'of/a/package';
Run Code Online (Sandbox Code Playgroud)

...给捆绑商一个package.anotherPart不需要的提示(如果它没有被导入,它不能被使用 - 对吗?),所以它不会打扰捆绑它.

要为Webpack 2启用此功能,您需要确保您的转换器不会吐出CommonJS模块.如果您正在使用es2015带有babel 的插件,则可以将其禁用为.babelrc:

{
  "presets": [
    ["es2015", { modules: false }],
  ]
}
Run Code Online (Sandbox Code Playgroud)

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

  • 也非常适合摇树http://www.2ality.com/2015/12/webpack-tree-shaking.html (2认同)

Mee*_*eri 22

当涉及异步或延迟加载时,import ()则更强大.看看我们何时以异步方式需要组件,然后我们import以某种异步方式使用它,就像const使用变量一样await.

const module = await import('./module.js');
Run Code Online (Sandbox Code Playgroud)

或者如果你想使用require()那么,

const converter = require('./converter');
Run Code Online (Sandbox Code Playgroud)

事情是import()实际上是异步的性质.如由neehar venugopal提到ReactConf,可以用它来动态加载反应客户端体系结构组件.

在路由方面也更好.当用户将特定网站连接到其特定组件时,这是使网络日志下载必要部分的一个特殊事情.例如,仪表板之前的登录页面不会下载仪表板的所有组件.因为需要当前即登录组件,只会下载.

同样export如下:ES6 export与CommonJS完全相同module.exports.

注意 - 如果您正在开发node.js项目,那么您必须严格使用,require()因为节点将抛出异常错误,invalid token 'import'就像您将使用一样import.因此节点不支持import语句.

更新 - 正如Dan Dascalescu所建议的:自v8.5.0(2017年9月发布)以来,node --experimental-modules index.mjs让您在import没有Babel的情况下使用.您可以(也应该)将您的npm包发布为本机ESModule,并具有require方式的向后兼容性.

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


小智 14

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

在2019年,84%的浏览器都支持ES6模块。虽然Node.js的把他们身后的--experimental模块的标志,也有一个方便的节点包叫ESM,这使得整合顺利。

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


cha*_*doo 7

I personally use import because, we can import the required methods, members by using import.

import {foo, bar} from "dep";
Run Code Online (Sandbox Code Playgroud)

FileName: dep.js

export foo function(){};
export const bar = 22
Run Code Online (Sandbox Code Playgroud)

Credit goes to Paul Shan. More info.

  • 您可以使用require做同样的事情! (5认同)
  • `const {a,b} = require('module.js'); `同样适用...如果导出`a`和`b` (3认同)

Yil*_*maz 5

  • ES 模块是静态的,这意味着导入是在每个模块的顶层以及任何控制流语句之外进行描述的。这是行不通的:

    if (condition) {
       import module1 from 'module1'
    }
    
    Run Code Online (Sandbox Code Playgroud)

但在commonjs中,是允许的:

if (condition) {
    module = require('module1')
}
Run Code Online (Sandbox Code Playgroud)
  • ES 模块在strict mode. 这意味着我们不必在每个文件的开头显式添加“use strict”语句。严格模式无法禁用;因此,我们不能使用未声明的变量或 with 语句或具有仅在非严格模式下可用的其他功能。strict mode是一种更安全的执行模式。

  • 在 ESM 中,一些重要的 CommonJS 引用没有定义。这些包括 require , exports , module.exports , __filename,__dirname

  • 我们可以使用标准导入语法从 ESM 导入 CommonJS 模块。但只有default exports工作:

       import packageName from 'commonjs-package' // Works
      import { moduleName } from 'commonjs-package' // Errors
    
    Run Code Online (Sandbox Code Playgroud)

但是,无法从 CommonJS 模块导入 ES 模块。

  • ESM 无法直接将 JSON 文件作为模块导入,这是 CommonJS 中经常使用的功能。这就是为什么在reactjs中fetch使用api。

    import data from './data.json' //fails
    
    Run Code Online (Sandbox Code Playgroud)