在我正在合作的项目中,我们有两个选择,我们可以使用哪个模块系统:
使用require导入模块,并使用module.exports和exports.foo导出。
使用ES6 import导入模块,并使用ES6 export导出
使用一个优于另一个是否有任何性能优势? 如果我们使用ES6模块而不是Node模块,还有什么我们应该知道的吗?
-
node --experimental-modules index.mjs允许您使用不带Babel的import并在Node 8.5.0+中工作。 您可以(也应该)将您的npm包发布为本机ESModule,并向后兼容旧的require方式。
Are there any performance benefits to using one over the other?
请记住,还没有本身支持ES6模块的JavaScript引擎。你说你自己在使用巴别塔。无论如何,Babel默认将import和export声明转换为CommonJS(require / module.exports)。因此,即使您使用ES6模块语法,如果您在Node中运行代码,也将使用CommonJS。
CommonJS和ES6模块之间存在技术差异,例如: CommonJS允许您动态加载模块。 ES6不允许这样做,但是有一个API正在开发中。
由于ES6模块是标准的一部分,我会使用它们。
-
我尝试将ES6 import与require一起使用,但它们的工作方式不同。 CommonJS只导出类本身,而只有一个类。 ES6导出就像有多个类一样,所以你必须使用.ClassName来获取导出的类。是否存在实际影响实施的其他差异
-
@Entei:好像你想要一个默认导出,而不是命名导出。 module.exports = ...;相当于export default ...。 exports.foo = ...相当于export var foo = ...;
-
值得注意的是,即使Babel最终将import转换为Node中的CommonJS,与Webpack 2 / Rollup(以及允许ES6树摇动的任何其他捆绑器)一起使用,也可以使用比同等数据小得多的文件。代码节点通过使用require完全解决,因为ES6允许导入/导出的静态分析。虽然这对Node(但是)没有什么影响,但如果代码最终将作为单个浏览器捆绑包结束,它肯定可以。
-
除非您需要进行动态导入
-
@LeeBenson实际上很多代码都不是这样。例如,请参阅此汇总/ lodash问题。
-
@aaaaaa - import无法帮助糟糕的图书馆设计。这不是魔术。
-
ES6模块是最新的V8,也是在旗帜后面的其他浏览器中。见:medium.com/dev-channel/…
-
你什么时候不在Node中运行你的代码?现在使用节点的NPM,是不是大多数,如果不是全部,JS?
-
@stackjlei什么时候嵌入网站?使用npm实际上并不意味着代码在节点中执行,这要归功于诸如webpack之类的模块捆绑器。
-
@FelixKling所以如果你使用的是节点服务器,那么CommonJS是免费的,但如果你使用npm作为前端,那么它是免费的,对吧?
-
esnext支持动态模块加载
-
从v8.5.0(一年多前发布)开始,node --experimemntal-modules index.mjs允许你使用import而不使用Babel。您可以(也应该)将您的npm包发布为本机ESModule,向后兼容旧的require方式。
您可能需要考虑以下几种用法/功能:
要求:
-
您可以在加载的模块名称不在的地方进行动态加载
预定义/静态,或仅在有条件地加载模块的情况下
它是"真正需要的"(取决于某些代码流)。
-
加载是
同步。这意味着如果你有多个require,它们就是
一个一个地加载和处理。
ES6进口:
-
您可以使用
命名导入以选择性地仅加载您需要的部分。这样可以
节省内存。
-
导入可以是异步的(在当前的ES6模块加载器中,它实际上是)并且可以执行得更好。
此外,Require模块系统不是基于标准的。现在ES6模块存在的可能性很小。将来,在各种实现中将对ES6模块提供原生支持,这在性能方面将是有利的。
-
是什么让你认为ES6导入是异步的?
-
@FelixKling - 各种观察的组合。使用JSPM(ES6 Module Loader ...)我注意到当导入修改了全局命名空间时,在其他导入中没有观察到这种效果(因为它们是异步发生的..这也可以在转换后的代码中看到)。此外,由于这是行为(1导入不影响其他行为),因此没有理由不这样做,因此它可能依赖于实现
-
你提到了非常重要的东西:模块加载器。虽然ES6提供了导入和导出语法,但它没有定义应该如何加载模块。重要的是声明是静态可分析的,因此可以在不执行代码的情况下确定依赖关系。这将允许模块加载器同步或异步加载模块。但ES6模块本身不是同步或异步的。
-
@FelixKling ES6模块加载器在OP中被标记,因此我认为它与答案相关。我还说基于观察异步是当前的行为,以及将来(在任何实现中)的可能性,因此这是一个需要考虑的相关点。你觉得这是错的吗?
-
我认为重要的是不要将模块系统/语法与模块加载器混淆。例如,如果您为节点开发,那么您可能无论如何都要将ES6模块编译为require,因此无论如何您都在使用Node的模块系统和加载器。
-
@Amit我的错误。我使用的是Babel,而不是ES6模块加载器
-
@kpimov那没关系。在Felix Kling的评论之后我无论如何都修改了答案。异步部分不保证可用(取决于实现),但可能会被使用。
-
@FelixKling--道格拉斯·克罗克福德在他的演讲中"更好的部分"youtu.be/_EF-FO63MXs?t=764似乎表明ES6模块导入将是完全异步的。
-
这个答案让我理解得更好,谢谢@Amit!
-
@Amit可以澄清一下"你可以动态加载,其中加载的模块名称不是预定义/静态的,或者只有在"真正需要"时才有条件地加载模块(取决于某些代码流)。"在es6模块中加载VS异步模块?
-
虽然require允许动态加载,但当您使用ES6导入时,webpack会发生树木抖动并消除未使用的代码。
-
require和import之间的主要区别是require会自动扫描node_modules来查找模块,但是来自ES6的import不会。大多数人使用babel编译导入和导出,这使得导入行为与require相同
主要优点是句法:
-
更多声明/紧凑语法
-
ES6模块基本上会使UMD(通用模块定义)过时 - 基本上消除了CommonJS和AMD(服务器与浏览器)之间的分裂。
您不太可能看到ES6模块的任何性能优势。即使在浏览器中完全支持ES6功能,您仍然需要一个额外的库来捆绑模块。
-
您是否可以澄清为什么即使浏览器具有完整的ES6模块支持,也需要捆绑器?
-
道歉,编辑更有意义。我的意思是导入/导出模块功能本身并未在任何浏览器中实现。仍需要转换器。
-
这对我来说似乎有些矛盾。如果有完全支持那么捆绑器的目的是什么? ES6规格中是否缺少某些内容?捆绑器实际上会做什么在完全支持的环境中不可用?
-
正如@snozza所说......"导入/导出模块功能并没有在任何浏览器中实现。还需要一个转换器"
-
据我了解,现在ES6浏览器支持意味着您可以在大多数现代浏览器中本地使用更简单的JavaScript模块语法(export和import)。除非您仍需要为IE9编写。如果我错过了一些明显的东西,请纠正我,但我可以在运行节点的http服务器的Chrome中使用ES6模块(比如说)而没有其他库。
-
您不再需要任何额外的库。从v8.5.0(一年多前发布)开始,node --experimemntal-modules index.mjs允许你使用import而不使用Babel。您可以(也应该)将您的npm包发布为本机ESModule,具有旧require方式的向后兼容性。许多浏览器本身也支持动态导入。
Are there any performance benefits to using one over the other?
目前的答案是否定的,因为当前的浏览器引擎都没有从ES6标准实现import/export。
一些比较图表http://kangax.github.io/compat-table/es6/不考虑这一点,所以当你看到Chrome的几乎所有绿色时,请小心。 ES6中的import关键字未被考虑在内。
换句话说,包括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。
-
实际上,有一个领域可以改善性能 - 捆绑尺寸。在Webpack 2 / Rollup构建过程中使用import可以通过"树摇动"未使用的模块/代码来减少由此产生的文件大小,否则这些模块/代码可能会在最终捆绑中结束。较小的文件大小=下载速度更快=在客户端上更快地执行/执行。
-
原因是地球上没有当前的浏览器本身允许import 关键字。或者这意味着您无法从JavaScript文件导入另一个JavaScript文件。这就是为什么你无法比较这两者的性能优势。但是,当然,像Webpack1 / 2或Browserify这样的工具可以处理压缩。他们是一对一的:gist.github.com/substack/68f8d502be42d5cd4942
-
你忽略了'树摇晃'。在你的要点链接中没有任何地方讨论树摇晃。使用ES6模块启用它,因为import和export是导入特定代码路径的静态声明,而require可以是动态的,因此捆绑在未使用的代码中。性能优势是间接的 - Webpack 2和/或Rollup可能会导致更小的捆绑包大小,下载速度更快,因此对最终用户(浏览器)来说更加快捷。这仅适用于所有代码都是用ES6模块编写的,因此可以静态分析导入。
-
我更新了答案@LeeBenson,我认为如果我们考虑来自浏览器引擎的原生支持,我们无法比较。即使在我们设置CommonJS模块之前,也可以使用Webpack实现方便的三个抖动选项,因为对于大多数实际应用程序,我们知道应该使用哪些模块。
-
你的答案是完全有效的,但我认为我们正在比较两个不同的特征。所有import/export都被转换为require,被授予。但是,在此步骤之前发生的事情可以被视为"性能"增强。示例:如果lodash是在ES6中编写的并且您import { omit } from lodash,则最终的包将仅包含"省略"而不包含其他实用程序,而简单的require('lodash')将导入所有内容。这将增加捆绑包大小,下载时间更长,从而降低性能。当然,这仅在浏览器上下文中有效。
-
"因为对于大多数真正的应用程序,我们知道应该使用哪些模块" - 这对于您自己的模块来说是正确的,但是当从npm导入包时,您可能对内部结构了解不多。如果使用ES6导入/导出编写源代码,并且只包含您需要的内容,则Webpack 2 / Roll up通常可以省略您不需要的内容,因为它可以在转换之前静态分析源代码。同样,我在谈论目标是浏览器包的情况,你关心的是该包的大小。我已经练习了5mb +减少到500kb。
-
我们在同一页面上,import表示法很方便,并且允许您更具体,哪些捆绑包喜欢。我现在不能指出任何可以为未动态加载的CommonJS模块做三次摇动的工具。但我想这可以通过对使用中的代码进行静态分析来实现。
-
这个答案已经过时了。所有现代浏览器都支持import,Chrome& Safari也支持动态导入。
使用ES6模块可以用于"树摇动";即启用Webpack 2,Rollup(或其他捆绑包)以识别未使用/导入的代码路径,因此不会将其放入生成的捆绑包中。这可以通过消除您永远不需要的代码来显着减少其文件大小,但默认情况下捆绑了CommonJS,因为Webpack等人无法知道是否需要它。
这是使用代码路径的静态分析完成的。
例如,使用:
1
| import { somePart } 'of/a/package'; |
...给捆绑者一个package.anotherPart不需要的提示(如果它没有导入,它不能被使用 - 对吗?),所以它不会打扰捆绑它。
要为Webpack 2启用此功能,您需要确保您的转换器不会吐出CommonJS模块。如果您使用带有babel的es2015插件,可以在.babelrc中禁用它,如下所示:
1 2 3 4 5
| {
"presets": [
["es2015", { modules: false }],
]
} |
汇总和其他人可能会有不同的工作方式 - 如果您有兴趣,请查看文档。
-
也很适合树摇晃2ality.com/2015/12/webpack-tree-shaking.html
当涉及异步或延迟加载时,import ()更强大。看看我们何时以异步方式需要组件,然后我们以const变量使用await以某种异步方式使用import。
1
| const module = await import('./module.js'); |
或者如果你想使用require()那么,
1
| const converter = require('./converter'); |
事实import()实际上是异步的。正如ReactConf中的neehar venugopal所提到的,您可以使用它来动态加载客户端架构的反应组件。
在路由方面也更好。当用户将特定网站连接到其特定组件时,这是使网络日志下载必要部分的一个特殊事情。例如仪表板之前的登录页面不会下载仪表板的所有组件。因为需要当前即登录组件,只会下载。
export也是如此:ES6 export与CommonJS module.exports完全相同。
注 - 如果您正在开发node.js项目,那么您必须严格使用require(),因为如果您将使用import,节点将抛出异常错误invalid token '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
最重要的是要知道ES6模块确实是官方标准,而CommonJS(Node.js)模块则不是。
在2019年,84%的浏览器支持ES6模块。 虽然Node.js将它们放在--experimental-modules标志后面,但是还有一个方便的节点包叫做esm,这使得集成变得平滑。
您可能在这些模块系统之间遇到的另一个问题是代码位置。 Node.js假定源保存在node_modules目录中,而大多数ES6模块部署在平面目录结构中。 这些并不容易协调,但可以通过使用安装前和安装后脚本来破解package.json文件来完成。 这是一个示例同构模块和一篇解释它是如何工作的文章。
我个人使用import,因为我们可以使用import导入所需的方法,成员。
1
| import {foo, bar} from"dep"; |
FileName:dep.js
1 2
| export foo function(){};
export const bar = 22 |
归功于Paul Shan。更多信息。
-
选的好!您是否也将npm软件包发布为本机ESModule,具有旧的require方式的向后兼容性?
-
你可以做同样的要求!
-
const {a,b} = require('module.js'); 也适用...如果您导出a和b