1330

In a project I am collaborating on, we have two choices on which module system we can use:

  1. Importing modules usingrequire, and exporting usingmodule.exports andexports.foo.
  2. Importing modules usingES6import, and exporting using ES6export

Are there any performance benefits to using one over the other? Is there anything else that we should know if we were to use ES6 modules over Node.js ones?

Peter Mortensen's user avatar
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
askedJul 11, 2015 at 7:19
kpimov's user avatar
2
  • 14
    node --experimental-modules index.mjs lets you useimport without Babel and works in Node 8.5.0+. You can (and should) alsopublish your npm packages as native ESModule, with backwards compatibility for the oldrequire way.CommentedJan 26, 2019 at 1:14
  • 1
    No necessary to use .mjs files, just use type: "module" in your package.json and put extension when importing only for your project files, that's itCommentedMay 24, 2021 at 21:13

11 Answers11

979
Update

Since Node.js v12 (April 2019), support for ES modules is enabled by default, and since Node.js v15 (October 2020) it's stable (seehere). Files, including Node.js modules, must either end in.mjs or the nearestpackage.json file must contain"type": "module". TheNode.js documentation has a ton more information, also about interoperability betweenCommonJS and ES modules.

Performance-wise there is always the chance that newer features are not as well optimized as existing features. However, since module files are only evaluated once, the performance aspect can probably be ignored. In the end, you have to run benchmarks to get a definite answer anyway.

ES modules can be loaded dynamically via theimport() function. Unlikerequire, this returns a promise.


Previous answer

Are there any performance benefits to using one over the other?

Keep in mind that there is no JavaScript engine yet that natively supports ES6 modules. You said yourself that you are usingBabel. Babel convertsimport andexport declaration to CommonJS (require/module.exports) by default anyway. So even if you use ES6 module syntax, you will be using CommonJS under the hood if you run the code in Node.js.

There are technical differences between CommonJS and ES6 modules, e.g., CommonJS allows you to load modules dynamically. ES6 doesn't allow this,but there is an API in development for that.

Since ES6 modules are part of the standard, I would use them.

Peter Mortensen's user avatar
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answeredJul 12, 2015 at 12:38
Felix Kling's user avatar
Sign up to request clarification or add additional context in comments.

12 Comments

@Entei: Seems like you want a default export, not a named export.module.exports = ...; is equivalent toexport default ....exports.foo = ... is equivalent toexport var foo = ...;
It's worth noting that even though Babel ultimately transpilesimport to CommonJS in Node, used alongside Webpack 2 / Rollup (and any other bundler that allows ES6 tree shaking), it's possible to wind up with a file that is significantly smaller than the equivalent code Node crunches through usingrequire exactlybecause of the fact ES6 allows static analysis of import/exports. Whilst this won't make a difference to Node (yet), it certainly can if the code is ultimately going to wind up as a single browser bundle.
ES6 Modules are in the latest V8 and are also arriving in other browsers behind flags. See:medium.com/dev-channel/…
@stackjlei when it’s embedded in a website? Using npm doesn’t actually mean that the code is executed in node, thanks to module bundlers such as webpack.
@Akhila: Added an update. Let me know if you think that suffices or if I should add more. Thank you for pinging me about this.
|
231

There are several usage / capabilities you might want to consider:

Require:

  • You can have dynamic loading where the loaded module name isn'tpredefined /static, or where you conditionally load a module only ifit's "truly required" (depending on certain code flow).
  • Loading issynchronous. That means if you have multiplerequires, they areloaded and processed one by one.

ES6 Imports:

  • You can usenamed imports to selectively load only the pieces you need. That cansave memory.
  • Import can be asynchronous (and in current ES6 Module Loader, it in fact is) and can perform a little better.

Also, the Require module system isn't standard based. It's is highly unlikely to become standard now that ES6 modules exist. In the future there will be native support for ES6 Modules in various implementations which will be advantageous in terms of performance.

answeredJul 11, 2015 at 8:49
Amit's user avatar

13 Comments

What makes you think ES6 imports are asynchronous?
@FelixKling - combination of various observations. Using JSPM (ES6 Module Loader...) I noticed that when an import modified the global namespace the effect isn't observed inside other imports (because they occur asynchronously.. This can also be seen in transpiled code). Also, since that is the behavior (1 import doesn't affect others) there no reason not to do so, so it could be implementation dependant
You mention something very important: module loader. While ES6 provides the import and export syntax, it does not define how modules should be loaded. The important part is that the declarations are statically analyzable, so that dependencies can be determined without executing the code. This would allow a module loader to either load a module synchronously or asynchronously. But ES6 modules by themselves are not synchronous or asynchronous.
@FelixKling ES6 module loader was tagged in the OP so I assume it makes it relevant to the answer. Also I stated that based on observations async is current behavior, as well as possibility in the future (in any implementation) so it's a relevant point to consider. Do you think it's wrong?
I think it's important not to conflate the module system/syntax with the module loader. E.g if you develop for node, then you are likely compiling ES6 modules torequire anyway, so you are using Node's module system and loader anyway.
|
152

As of right now ES6 import, export isalways compiled to CommonJS, so there isno benefit using one or other. Although usage of ES6 is recommended since it should be advantageous when native support from browsers released. The reason being, you can import partials from one file while with CommonJS you have to require all of the file.

ES6 →import, export default, export

CommonJS →require, module.exports, exports.foo

Below is common usage of those.

ES6 export default

// hello.jsfunction hello() {  return 'hello'}export default hello// app.jsimport hello from './hello'hello() // returns hello

ES6 export multiple and import multiple

// hello.jsfunction hello1() {  return 'hello1'}function hello2() {  return 'hello2'}export { hello1, hello2 }// app.jsimport { hello1, hello2 } from './hello'hello1()  // returns hello1hello2()  // returns hello2

CommonJS module.exports

// hello.jsfunction hello() {  return 'hello'}module.exports = hello// app.jsconst hello = require('./hello')hello()   // returns hello

CommonJS module.exports multiple

// hello.jsfunction hello1() {  return 'hello1'}function hello2() {  return 'hello2'}module.exports = {  hello1,  hello2}// app.jsconst hello = require('./hello')hello.hello1()   // returns hello1hello.hello2()   // returns hello2
answeredFeb 21, 2020 at 3:41
Hasan Sefa Ozalp's user avatar

2 Comments

You actually can useObject Destructuring when using CommonJS require as well. So you could have:const { hello1, hello2 } = require("./hello"); and it will besomewhat similar to using import/export.
This is by far the best answer as it provides not only the description, but also the actual code snippets.
48

The main advantages are syntactic:

  • More declarative/compact syntax
  • ES6 modules will basically make UMD (Universal Module Definition) obsolete - essentially removes the schism between CommonJS and AMD (server vs browser).

You are unlikely to see any performance benefits with ES6 modules. You will still need an extra library to bundle the modules, even when there is full support for ES6 features in the browser.

answeredJul 11, 2015 at 8:15
snozza's user avatar

6 Comments

Could you clarify why one needs a bundler even when browsers has full ES6 module support?
Apologies, edited to make more sense. I meant that the import/export modules feature is not implemented in any browsers natively. A transpiler is still required.
It seems a bit contradictory frased to me. If there isfull support then what is the purpose of the bundler? Is there something missing in the ES6 spec? What would the bundler actually do that isn't available in afully supported environment?
As @snozza said..."the import/export modules feature is not implemented in any browsers naively. A transpiler is still required"
You no longer need any extra libraries. Since v8.5.0 (released more than a year ago),node --experimemntal-modules index.mjs lets you useimport without Babel. You can (and should) alsopublish your npm packages as native ESModule, with backwards compatibility for the oldrequire way. Many browsers also supportdynamic imports natively.
|
39

Are there any performance benefits to using one over the other?

The current answer is no, because none of the current browser engines implementsimport/export from the ES6 standard.

Some comparison chartshttp://kangax.github.io/compat-table/es6/ don't take this into account, so when you see almost all greens for Chrome, just be careful.import keyword from ES6 hasn't been taken into account.

In other words, current browser engines including V8 cannot importnew JavaScript file from themain JavaScript file via any JavaScript directive.

( We may be still justa few bugs away or years away until V8 implements that according to the ES6 specification. )

Thisdocument is what we need, and thisdocument is what we must obey.

And the ES6 standard said that the module dependencies should be there before we read the module like in the programming language C, where we had (headers).h files.

This is a good and well-tested structure, and I am sure the experts that created the ES6 standard had that in mind.

This is what enables Webpack or other package bundlers to optimize the bundle in somespecial cases, and reduce some dependencies from the bundle that are not needed. But in cases we have perfect dependencies this will never happen.

It will need some time untilimport/export native support goes live, and therequire keyword will not go anywhere for a long time.

What isrequire?

This isnode.js way to load modules. (https://github.com/nodejs/node )

Node uses system-level methods to read files. You basically rely on that when usingrequire.require will end in some system call likeuv_fs_open (depends on the end system, Linux, Mac, Windows) to load JavaScript file/module.

To check that this is true, try to use Babel.js, and you will see that theimport keyword will be converted intorequire.

enter image description here

answeredNov 5, 2016 at 15:48
prosti's user avatar

8 Comments

Actually, there's one area where performancecould be improved -- bundle size. Usingimport in a Webpack 2 / Rollup build process can potentially reduce the resulting file size by 'tree shaking' unused modules/code, that might otherwise wind up in the final bundle. Smaller file size = faster to download = faster to init/execute on the client.
the reasoning was no current browser on the planet earth allows theimport keyword natively. Or this means you cannot import another JavaScript file from a JavaScript file. This is why you cannot compare performance benefits of these two. But of course, tools like Webpack1/2 or Browserify can deal with compression. They are neck to neck:gist.github.com/substack/68f8d502be42d5cd4942
You're overlooking 'tree shaking'. Nowhere in your gist link is tree shaking discussed. Using ES6 modules enables it, becauseimport andexport are static declarations that import a specific code path, whereasrequire can be dynamic and thus bundle in code that's not used. The performance benefit is indirect-- Webpack 2 and/or Rollup canpotentially result in smaller bundle sizes that are faster to download, and therefore appear snappier to the end user (of a browser). This only works if all code is written in ES6 modules and therefore imports can be statically analysed.
I updated the answer @LeeBenson, I think if we consider the native support from browser engines we cannot compare yet. What comes as handy three shaking option using the Webpack, may also be achieved even before we set the CommonJS modules, since for most of the real applications we know what modules should be used.
Your answer is totally valid, but I think we're comparing two different characteristics.Allimport/export is converted torequire, granted. But what happensbefore this step could be considered "performance" enhancing. Example: Iflodash is written in ES6 and youimport { omit } from lodash, the ultimate bundle will ONLY contain 'omit' and not the other utilities, whereas a simplerequire('lodash') will import everything. This will increase the bundle size, take longer to download, and therefore decrease performance. This is only valid in a browser context, of course.
|
38

Using ES6 modules can be useful for 'tree shaking'; i.e. enabling Webpack 2, Rollup (or other bundlers) to identify code paths that are not used/imported, and therefore don't make it into the resulting bundle. This can significantly reduce its file size by eliminating code you'll never need, but with CommonJS is bundled by default because Webpack et al have no way of knowing whether it's needed.

This is done using static analysis of the code path.

For example, using:

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

... gives the bundler a hint thatpackage.anotherPart isn't required (if it's not imported, it can't be used- right?), so it won't bother bundling it.

To enable this for Webpack 2, you need to ensure that your transpiler isn't spitting out CommonJS modules. If you're using thees2015 plug-in with babel, you can disable it in your.babelrc like so:

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

Rollup and others may work differently - view the docs if you're interested.

answeredOct 27, 2016 at 15:45
Lee Benson's user avatar

2 Comments

What does happen withrequire then?
33

When it comes to async or maybe lazy loading, thenimport () is much more powerful. See when we require the component in asynchronous way, then we useimport it in some async manner as inconst variable usingawait.

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

Or if you want to userequire() then,

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

Thing isimport() is actually async in nature. As mentioned by neehar venugopal inReactConf, you can use it to dynamically load React components for client-side architecture.

Also it is way better when it comes to Routing. That is the one special thing that makes network log to download a necessary part when user connects to specific website to its specific component. E.g., login page before dashboard wouldn't download all components of dashboard. Because what is needed currently, i.e., the login component, only that will be downloaded.

The same goes forexport: ES6export are exactly same as for CommonJSmodule.exports.

Note: If you are developing a Node.js project, then you have to strictly userequire() as Node.js will throw exception error asinvalid token 'import' if you will useimport. So Node.js does not support import statements.

Update - As suggested byDan Dascalescu: Since v8.5.0 (released Sep 2017),node --experimental-modules index.mjs lets you useimport without Babel. You can (and should) alsopublish your npm packages as a native ES Module, with backwards compatibility for the oldrequire way.

See this for more clearance where to use async imports:Neehar Venugopal - A Beginner's Guide to Code Splitting Your React App - React Conf 2017

Peter Mortensen's user avatar
Peter Mortensen
31.5k22 gold badges110 silver badges134 bronze badges
answeredNov 28, 2017 at 2:37
Meet Zaveri's user avatar

2 Comments

So will the require be sync and wait?
Can say factually!
19

The most important thing to know is that ES6 modules are, indeed, an official standard, while CommonJS (Node.js) modules are not.

In 2019, ES6 modules are supported by84% of browsers. While Node.js puts them behind an--experimental-modules flag, there is also a convenient node package calledesm, which makes the integration smooth.

Another issue you're likely to run into between these module systems is code location. Node.js assumes source is kept in anode_modules directory, while most ES6 modules are deployed in a flat directory structure. These are not easy to reconcile, but it can be done by hacking yourpackage.json file with pre and post installation scripts. Here is an exampleisomorphic module and anarticle explaining how it works.

answeredApr 14, 2019 at 15:25
isysd's user avatar

Comments

11

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

import {foo, bar} from "dep";

FileName: dep.js

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

Credit goes to Paul Shan.More info.

answeredNov 22, 2017 at 7:04
chandoo's user avatar

4 Comments

you can do the same with require!
const {a,b} = require('module.js'); works as well ... if you exporta andb
module.exports = { a: ()={}, b: 22 } - The second part of @BananaAcid respones
5
  • ES modules are static, which means that imports are described at the top level of every module and outside any control flow statement. This will not work:

    if (condition) {   import module1 from 'module1'}

But it in commonjs, it is allowed:

if (condition) {    module = require('module1')}
  • ES modules run implicitly instrict mode. This means that we don't have to explicitly add the "use strict" statements at the beginning of every file. Strict mode cannot be disabled; therefore, we cannot use undeclared variables or the with statement or have other features that are only available in non-strict mode.strict mode is a safer execution mode.

  • In ESM, some important CommonJS references are not defined. These includerequire , exports , module.exports , __filename, and__dirname.

  • We can import CommonJS modules from ESM by using the standard import syntax. But onlydefault exports work:

       import packageName from 'commonjs-package' // Works  import { moduleName } from 'commonjs-package' // Errors

But, it is not possible to import ES modules from CommonJS modules.

  • ESM cannot import JSON files directly as modules, a feature that is used quite frequently with CommonJS. That is why in reactjsfetch api is used.

    import data from './data.json' //fails
answeredJan 26, 2022 at 0:47
Yilmaz's user avatar

3 Comments

Youcan import module conditionally withdynamic import.
You can import JSON files directly now.
2

Not sure why (probably optimization - lazy loading?) is it working like that, but I have noticed thatimport may not parse code if imported modules are not used.
Which may not be expected behaviour in some cases.

Take hated Foo class as our sample dependency.

foo.ts

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

For example:

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"})();

On the other hand:

index.ts

import Foo from './foo'typeof Foo; // any use case// prints "Foo loaded"
answeredNov 30, 2019 at 2:14
l00k's user avatar

Comments

Protected question. To answer this question, you need to have at least 10 reputation on this site (not counting theassociation bonus). The reputation requirement helps protect this question from spam and non-answer activity.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.