使用 Node.js require 与 ES6 import/export
Using Node.js require vs. ES6 import/export
在我正在合作的项目中,我们有两种选择可以使用的模块系统:
- 使用
require
导入模块,使用 module.exports
和 exports.foo
导出模块。
- 使用 ES6 导入模块
import
,并使用 ES6 导出 export
使用一个比另一个有任何性能优势吗?如果我们要使用 ES6 模块而不是 Node 模块,还有什么我们应该知道的吗?
语法上的主要优点:
- 更多 declarative/compact 语法
- ES6 模块基本上将使 UMD(通用模块定义)过时 - 基本上消除了 CommonJS 和 AMD(服务器与浏览器)之间的分裂。
您不太可能看到 ES6 模块的任何性能优势。即使浏览器完全支持 ES6 功能,您仍然需要一个额外的库来捆绑模块。
您可能需要考虑几种用途/功能:
要求:
- 您可以在没有加载模块名称的情况下进行动态加载
预定义的/静态的,或者只有在以下情况下才有条件地加载模块的地方
它是 "truly required"(取决于特定的代码流)。
- 正在加载
同步。这意味着如果您有多个
require
,它们是
一个一个加载处理。
ES6 导入:
- 你可以使用
命名导入以选择性地仅加载您需要的部分。这样可以
节省内存。
- 导入可以是异步的(在当前的 ES6 模块加载器中,它实际上是异步的)并且可以执行得更好一些。
此外,Require 模块系统不是基于标准的。现在 ES6 模块已经存在,它不太可能成为标准。未来各种实现都会原生支持ES6 Modules,在性能上会有优势
更新
自 Node v12(2019 年 4 月)起,默认启用对 ES 模块的支持,自 Node v15(2020 年 10 月)起,它是稳定的(参见 here). Files including node modules must either end in .mjs
or the nearest package.json
file must contain "type": "module"
. The Node documentation 有更多信息,还有关于 CommonJS 之间的互操作和 ES 模块。
在性能方面,新功能总是有可能不如现有功能优化得那么好。然而,由于模块文件只被评估一次,性能方面可能会被忽略。最后你必须 运行 基准测试才能得到一个明确的答案。
ES模块可以通过import()
函数动态加载。与 require
不同,这 returns 是一个承诺。
上一个回答
Are there any performance benefits to using one over the other?
请记住,目前还没有 JavaScript 原生支持 ES6 模块的引擎。你自己说你正在使用 Babel。无论如何,Babel 默认将 import
和 export
声明转换为 CommonJS (require
/module.exports
)。因此,即使您使用 ES6 模块语法,如果您 运行 Node.
中的代码,您也会在幕后使用 CommonJS
CommonJS 和 ES6 模块之间存在技术差异,例如CommonJS 允许您动态加载模块。 ES6 不允许这样做,but there is an API in development for that.
由于 ES6 模块是标准的一部分,我会使用它们。
使用 ES6 模块对 'tree shaking' 很有用;即启用 Webpack 2、Rollup(或其他捆绑器)来识别不是 used/imported 的代码路径,因此不要将其放入生成的捆绑包中。这可以通过消除您永远不需要的代码来显着减小其文件大小,但默认情况下与 CommonJS 捆绑在一起,因为 Webpack 等人无法知道是否需要它。
这是使用代码路径的静态分析完成的。
例如,使用:
import { somePart } 'of/a/package';
... 向捆绑器提示不需要 package.anotherPart
(如果未导入,则无法使用 - 对吗?),因此它不会打扰捆绑它。
要为 Webpack 2 启用此功能,您需要确保您的转译器不会吐出 CommonJS 模块。如果你在 babel 中使用 es2015
插件,你可以在你的 .babelrc
中禁用它,如下所示:
{
"presets": [
["es2015", { modules: false }],
]
}
Rollup 和其他人的工作方式可能有所不同 - 如果您有兴趣,请查看文档。
Are there any performance benefits to using one over the other?
目前的答案是否定的,因为 none 当前的浏览器引擎实现了 import/export
ES6 标准。
一些比较图表 http://kangax.github.io/compat-table/es6/ 没有考虑到这一点,所以当您看到 Chrome 几乎所有果岭时,请小心。 import
ES6 中的关键字未被考虑在内。
换句话说,包括V8在内的当前浏览器引擎无法从主JavaScript文件[=59]导入新JavaScript文件 =] 通过任何 JavaScript 指令。
(距离 V8 根据 ES6 规范实现它可能还需要 a few bugs away 或几年的时间。)
这个document is what we need, and this document是我们必须遵守的
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 file/module.
要检查这是真的,请尝试使用 Babel.js,您会看到 import
关键字将转换为 require
。
我个人使用 import,因为我们可以使用 import 来导入所需的方法和成员。
import {foo, bar} from "dep";
文件名: dep.js
export foo function(){};
export const bar = 22
感谢 Paul Shan。 More info.
当谈到异步或延迟加载时,import ()
更强大。看看我们何时以异步方式需要组件,然后我们以某种异步方式使用 import
它,就像在 const
变量中使用 await
.
const module = await import('./module.js');
或者如果你想使用 require()
那么,
const converter = require('./converter');
事情是 import()
实际上是异步的。正如 neehar venugopal 在 ReactConf 中提到的,您可以使用它为客户端架构动态加载 React 组件。
在路由方面也更好。当用户连接到特定网站到其特定组件时,这是使网络日志下载必要部分的一件特殊事情。例如仪表板之前的登录页面不会下载仪表板的所有组件。因为当前需要的是登录组件,所以只会下载它。
export
也是如此:ES6 export
与 CommonJS module.exports
.
完全相同
注意 - 如果您正在开发一个 node.js 项目,那么您必须严格使用 require()
因为节点会抛出异常如果您将使用 import
,则错误为 invalid token 'import'
。所以节点不支持导入语句。
更新 - 正如 Dan Dascalescu: Since v8.5.0 (released Sep 2017), node --experimental-modules index.mjs
lets you use import
without Babel. You can (and should) also publish your npm packages as native ESModule, with backwards compatibility 对旧 require
方式的建议。
查看此内容以获得更多使用异步导入的许可 - https://www.youtube.com/watch?v=bb6RCrDaxhw
最重要的是要知道 ES6 模块确实是官方标准,而 CommonJS (Node.js) 模块不是。
2019年84% of browsers. While Node.js puts them behind an --experimental-modules flag, there is also a convenient node package called esm支持ES6模块,集成顺畅
您可能 运行 在这些模块系统之间遇到的另一个问题是代码位置。 Node.js 假定源代码保存在 node_modules
目录中,而大多数 ES6 模块部署在平面目录结构中。这些不容易调和,但可以通过使用预安装脚本和 post 安装脚本破解您的 package.json
文件来完成。这是一个示例 isomorphic module and an article 解释它是如何工作的。
不确定为什么(可能是优化 - 延迟加载?)它是这样工作的,但我注意到如果不使用导入的模块,import
可能无法解析代码。
在某些情况下,这可能不是预期的行为。
将讨厌的 Foo class 作为我们的示例依赖。
foo.ts
export default class Foo {}
console.log('Foo loaded');
例如:
index.ts
import Foo from './foo'
// prints nothing
index.ts
const Foo = require('./foo').default;
// prints "Foo loaded"
index.ts
(async () => {
const FooPack = await import('./foo');
// prints "Foo loaded"
})();
另一方面:
index.ts
import Foo from './foo'
typeof Foo; // any use case
// prints "Foo loaded"
截至目前 ES6 导入、导出为 always compiled to 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
ES 模块是静态的,这意味着导入是在每个模块的顶层描述的,并且在任何控制流语句之外。这行不通:
if (condition) {
import module1 from 'module1'
}
但是在commonjs中,是允许的:
if (condition) {
module = require('module1')
}
ES 模块 运行 隐含在 strict mode
中。这意味着我们不必在每个文件的开头显式添加“use strict”语句。不能禁用严格模式;因此,我们不能使用未声明的变量或 with 语句或具有仅在 non-strict 模式下可用的其他功能。 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
但是,无法从 CommonJS 模块导入 ES 模块。
ESM 无法将 JSON 文件直接作为模块导入,这是 CommonJS 经常使用的功能。这就是为什么在 reactjs 中使用 fetch
api。
import data from './data.json' //fails
在我正在合作的项目中,我们有两种选择可以使用的模块系统:
- 使用
require
导入模块,使用module.exports
和exports.foo
导出模块。 - 使用 ES6 导入模块
import
,并使用 ES6 导出export
使用一个比另一个有任何性能优势吗?如果我们要使用 ES6 模块而不是 Node 模块,还有什么我们应该知道的吗?
语法上的主要优点:
- 更多 declarative/compact 语法
- ES6 模块基本上将使 UMD(通用模块定义)过时 - 基本上消除了 CommonJS 和 AMD(服务器与浏览器)之间的分裂。
您不太可能看到 ES6 模块的任何性能优势。即使浏览器完全支持 ES6 功能,您仍然需要一个额外的库来捆绑模块。
您可能需要考虑几种用途/功能:
要求:
- 您可以在没有加载模块名称的情况下进行动态加载 预定义的/静态的,或者只有在以下情况下才有条件地加载模块的地方 它是 "truly required"(取决于特定的代码流)。
- 正在加载
同步。这意味着如果您有多个
require
,它们是 一个一个加载处理。
ES6 导入:
- 你可以使用 命名导入以选择性地仅加载您需要的部分。这样可以 节省内存。
- 导入可以是异步的(在当前的 ES6 模块加载器中,它实际上是异步的)并且可以执行得更好一些。
此外,Require 模块系统不是基于标准的。现在 ES6 模块已经存在,它不太可能成为标准。未来各种实现都会原生支持ES6 Modules,在性能上会有优势
更新
自 Node v12(2019 年 4 月)起,默认启用对 ES 模块的支持,自 Node v15(2020 年 10 月)起,它是稳定的(参见 here). Files including node modules must either end in .mjs
or the nearest package.json
file must contain "type": "module"
. The Node documentation 有更多信息,还有关于 CommonJS 之间的互操作和 ES 模块。
在性能方面,新功能总是有可能不如现有功能优化得那么好。然而,由于模块文件只被评估一次,性能方面可能会被忽略。最后你必须 运行 基准测试才能得到一个明确的答案。
ES模块可以通过import()
函数动态加载。与 require
不同,这 returns 是一个承诺。
上一个回答
Are there any performance benefits to using one over the other?
请记住,目前还没有 JavaScript 原生支持 ES6 模块的引擎。你自己说你正在使用 Babel。无论如何,Babel 默认将 import
和 export
声明转换为 CommonJS (require
/module.exports
)。因此,即使您使用 ES6 模块语法,如果您 运行 Node.
CommonJS 和 ES6 模块之间存在技术差异,例如CommonJS 允许您动态加载模块。 ES6 不允许这样做,but there is an API in development for that.
由于 ES6 模块是标准的一部分,我会使用它们。
使用 ES6 模块对 'tree shaking' 很有用;即启用 Webpack 2、Rollup(或其他捆绑器)来识别不是 used/imported 的代码路径,因此不要将其放入生成的捆绑包中。这可以通过消除您永远不需要的代码来显着减小其文件大小,但默认情况下与 CommonJS 捆绑在一起,因为 Webpack 等人无法知道是否需要它。
这是使用代码路径的静态分析完成的。
例如,使用:
import { somePart } 'of/a/package';
... 向捆绑器提示不需要 package.anotherPart
(如果未导入,则无法使用 - 对吗?),因此它不会打扰捆绑它。
要为 Webpack 2 启用此功能,您需要确保您的转译器不会吐出 CommonJS 模块。如果你在 babel 中使用 es2015
插件,你可以在你的 .babelrc
中禁用它,如下所示:
{
"presets": [
["es2015", { modules: false }],
]
}
Rollup 和其他人的工作方式可能有所不同 - 如果您有兴趣,请查看文档。
Are there any performance benefits to using one over the other?
目前的答案是否定的,因为 none 当前的浏览器引擎实现了 import/export
ES6 标准。
一些比较图表 http://kangax.github.io/compat-table/es6/ 没有考虑到这一点,所以当您看到 Chrome 几乎所有果岭时,请小心。 import
ES6 中的关键字未被考虑在内。
换句话说,包括V8在内的当前浏览器引擎无法从主JavaScript文件[=59]导入新JavaScript文件 =] 通过任何 JavaScript 指令。
(距离 V8 根据 ES6 规范实现它可能还需要 a few bugs away 或几年的时间。)
这个document is what we need, and this document是我们必须遵守的
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 file/module.
要检查这是真的,请尝试使用 Babel.js,您会看到 import
关键字将转换为 require
。
我个人使用 import,因为我们可以使用 import 来导入所需的方法和成员。
import {foo, bar} from "dep";
文件名: dep.js
export foo function(){};
export const bar = 22
感谢 Paul Shan。 More info.
当谈到异步或延迟加载时,import ()
更强大。看看我们何时以异步方式需要组件,然后我们以某种异步方式使用 import
它,就像在 const
变量中使用 await
.
const module = await import('./module.js');
或者如果你想使用 require()
那么,
const converter = require('./converter');
事情是 import()
实际上是异步的。正如 neehar venugopal 在 ReactConf 中提到的,您可以使用它为客户端架构动态加载 React 组件。
在路由方面也更好。当用户连接到特定网站到其特定组件时,这是使网络日志下载必要部分的一件特殊事情。例如仪表板之前的登录页面不会下载仪表板的所有组件。因为当前需要的是登录组件,所以只会下载它。
export
也是如此:ES6 export
与 CommonJS module.exports
.
注意 - 如果您正在开发一个 node.js 项目,那么您必须严格使用 require()
因为节点会抛出异常如果您将使用 import
,则错误为 invalid token 'import'
。所以节点不支持导入语句。
更新 - 正如 Dan Dascalescu: Since v8.5.0 (released Sep 2017), node --experimental-modules index.mjs
lets you use import
without Babel. You can (and should) also publish your npm packages as native ESModule, with backwards compatibility 对旧 require
方式的建议。
查看此内容以获得更多使用异步导入的许可 - https://www.youtube.com/watch?v=bb6RCrDaxhw
最重要的是要知道 ES6 模块确实是官方标准,而 CommonJS (Node.js) 模块不是。
2019年84% of browsers. While Node.js puts them behind an --experimental-modules flag, there is also a convenient node package called esm支持ES6模块,集成顺畅
您可能 运行 在这些模块系统之间遇到的另一个问题是代码位置。 Node.js 假定源代码保存在 node_modules
目录中,而大多数 ES6 模块部署在平面目录结构中。这些不容易调和,但可以通过使用预安装脚本和 post 安装脚本破解您的 package.json
文件来完成。这是一个示例 isomorphic module and an article 解释它是如何工作的。
不确定为什么(可能是优化 - 延迟加载?)它是这样工作的,但我注意到如果不使用导入的模块,import
可能无法解析代码。
在某些情况下,这可能不是预期的行为。
将讨厌的 Foo class 作为我们的示例依赖。
foo.ts
export default class Foo {}
console.log('Foo loaded');
例如:
index.ts
import Foo from './foo'
// prints nothing
index.ts
const Foo = require('./foo').default;
// prints "Foo loaded"
index.ts
(async () => {
const FooPack = await import('./foo');
// prints "Foo loaded"
})();
另一方面:
index.ts
import Foo from './foo'
typeof Foo; // any use case
// prints "Foo loaded"
截至目前 ES6 导入、导出为 always compiled to 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
ES 模块是静态的,这意味着导入是在每个模块的顶层描述的,并且在任何控制流语句之外。这行不通:
if (condition) { import module1 from 'module1' }
但是在commonjs中,是允许的:
if (condition) {
module = require('module1')
}
ES 模块 运行 隐含在
strict mode
中。这意味着我们不必在每个文件的开头显式添加“use strict”语句。不能禁用严格模式;因此,我们不能使用未声明的变量或 with 语句或具有仅在 non-strict 模式下可用的其他功能。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
但是,无法从 CommonJS 模块导入 ES 模块。
ESM 无法将 JSON 文件直接作为模块导入,这是 CommonJS 经常使用的功能。这就是为什么在 reactjs 中使用
fetch
api。import data from './data.json' //fails