[javascript] Using Node.js require vs. ES6 import/export

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

  1. Importing modules using require, and exporting using module.exports and exports.foo.
  2. Importing modules using ES6 import, and exporting using ES6 export

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 ones?

This question is related to javascript node.js ecmascript-6 babeljs

The answer is


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.


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

Require:

  • You can have dynamic loading where the loaded module name isn't predefined /static, or where you conditionally load a module only if it's "truly required" (depending on certain code flow).
  • Loading is synchronous. That means if you have multiple requires, they are loaded and processed one by one.

ES6 Imports:

  • You can use named imports to selectively load only the pieces you need. That can save 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.


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

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

Some comparison charts http://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 import new JavaScript file from the main JavaScript file via any JavaScript directive.

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

This document is what we need, and this document 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 some special 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 until import/export native support goes live, and the require keyword will not go anywhere for a long time.

What is require?

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

Node uses system-level methods to read files. You basically rely on that when using require. require will end in some system call like uv_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 the import keyword will be converted into require.

enter image description here


When it comes to async or maybe lazy loading, then import () is much more powerful. See when we require the component in asynchronous way, then we use import it in some async manner as in const variable using await.

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

Or if you want to use require() then,

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

Thing is import() is actually async in nature. As mentioned by neehar venugopal in ReactConf, 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 current i.e. login component, that only will be downloaded.

Same goes for export : ES6 export are exactly same as for CommonJS module.exports.

NOTE - If you are developing a node.js project, then you have to strictly use require() as node will throw exception error as invalid token 'import' if you will use import . So node does not support import statements.

UPDATE - As suggested by 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 for the old require way.

See this for more clearance where to use async imports - https://www.youtube.com/watch?v=bb6RCrDaxhw


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.


As of right now ES6 import, export is always compiled to CommonJS, so there is no 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.js
function hello() {
  return 'hello'
}
export default hello

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

ES6 export multiple and import multiple

// 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 multiple

// 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

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 by 84% of browsers. While Node.js puts them behind an --experimental-modules flag, there is also a convenient node package called esm, 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 a node_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 your package.json file with pre and post installation scripts. Here is an example isomorphic module and an article explaining how it works.


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 that package.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 the es2015 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.


Not sure why (probably optimization - lazy loading?) is it working like that, but I have noticed that import 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"

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to node.js

Hide Signs that Meteor.js was Used Querying date field in MongoDB with Mongoose SyntaxError: Cannot use import statement outside a module Server Discovery And Monitoring engine is deprecated How to fix ReferenceError: primordials is not defined in node UnhandledPromiseRejectionWarning: This error originated either by throwing inside of an async function without a catch block dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib error running php after installing node with brew on Mac internal/modules/cjs/loader.js:582 throw err DeprecationWarning: Buffer() is deprecated due to security and usability issues when I move my script to another server Please run `npm cache clean`

Examples related to ecmascript-6

"Uncaught SyntaxError: Cannot use import statement outside a module" when importing ECMAScript 6 where is create-react-app webpack config and files? Can (a== 1 && a ==2 && a==3) ever evaluate to true? How do I fix "Expected to return a value at the end of arrow function" warning? Enums in Javascript with ES6 Home does not contain an export named Home How to scroll to an element? How to update nested state properties in React eslint: error Parsing error: The keyword 'const' is reserved Node.js ES6 classes with require

Examples related to babeljs

Support for the experimental syntax 'classProperties' isn't currently enabled Export multiple classes in ES6 modules Getting Unexpected Token Export 'import' and 'export' may only appear at the top level Dynamically add child components in React Call async/await functions in parallel Correct way to import lodash Babel command not found ES6 exporting/importing in index file "unexpected token import" in Nodejs5 and babel?