ECMAScript (ESM) modules are a newer, standardized way to split complex code systems and reuse code in NodeJS. While CommonJS (CJS) modules are also available, ECMAScript modules have distinct advantages. For example, ESM allows tree-shaking, eliminates unused code, and minimizes file size. When choosing between module types, it's crucial to know the differences. ECMAScript modules offer a modern approach to code organization and should be a go-to for developers looking to streamline their projects.
ECMAScript modules play a crucial role in the execution of JavaScript modules in browsers, allowing for more efficient loading and better code organization, especially for larger web applications. Unlike CommonJS modules, ESM is natively supported by modern browsers and the latest versions of NodeJS.
What are ECMAScript modules
ECMAScript Modules (ESM) are a modern and standardized approach to organizing and reusing code in JavaScript applications. To use ECMAScript modules in NodeJS, you need to specify the file extension as .mjs. It's important to note that ESM files require the .mjs file extension. The .mjs extension distinguishes ESM files from CommonJS modules that use the .js extension. This requirement exists because ESM modules use a different syntax for importing and exporting code which is incompatible with CommonJS syntax.
For example, if you have a file named main.mjs, which imports code from another file named another.mjs, you should write the following import statement at the top of main.mjs file.
// main.mjs
import { functionFromAnotherModule } from './another.mjs';In the another.mjs file, you can export functionFromAnotherModule like this:
// another.mjs
export function functionFromAnotherModule() {
// Function code...
}Importing modules
Importing modules is the most used feature. You will always import some modules, to solve your tasks. Let's look at how you can do so:
import { URL } from 'node:url';
const mysiteUrl = new URL('https://mysupersite.com/about#me');
console.log(mysiteUrl.hash)
// #meTo import the module, use the import operator with from. Import the URL class from the node URL library. Then create a new URL with the URL class to call hash object value of your new URL object. It will return #me as the hash of the source URL.
Now, let's take a look at importing custom modules. Here you can see how the animals array is imported from the animals.mjs module.
import animals from "./animals.mjs";
console.log(animals);
// [ 'cat', 'dog', 'bird' ]Sometimes you need to import an entire module, such as animals. Or you can import just a part of a module, like with the URL class. To figure out the difference, let's try exporting modules.
Exporting modules
Exporting modules is as easy as importing them and it is very similar to CommonJS syntax. You can export the animals array using the export default operator:
// animals.mjs
export default [
'cat', 'dog', 'bird'
];By using the default keyword to export modules, you don't need to give a name to the exported item. It will be exported by default. For now, to import this module, use import animals from './animals.mjs'.
Be careful while using the export default operator. It should be the only export from a module. And don't forget to give a proper name to the module file. This can help you understand what exactly will be imported.
Sometimes it is necessary to import several items from one module. For example, an object and a function at the same time. Let's export the cat object and the getCatVoice() function:
// cat.mjs
export const cat = {
name: 'Bob',
voice: 'meow',
colors: ['black', 'orange'],
};
export const getCatVoice = (cat) => {
return cat.voice;
};You can import the cat object and the getCatVoice() function as shown in the index.mjs file below. The getCatVoice() function is called by passing the cat object in it.
// index.mjs
import { getCatVoice, cat } from "./cat.mjs";
console.log(getCatVoice(cat));
// meowImport as
Another important feature of importing ESM modules is the ability to import all of the code from a module under a single name. This is a simpler approach compared to importing individual functions or variables one by one.
// animals.mjs
export default [
'cat', 'dog', 'bird'
];
export const cat = {
name: 'Bob',
voice: 'meow',
colors: ['black', 'orange'],
};
export const getCatVoice = (cat) => {
return cat.voice;
};// index.mjs
import * as animals from './animals.mjs';
console.log(animals.default);
// [ 'cat', 'dog', 'bird' ]
console.log(animals.cat);
// { name: 'Bob', voice: 'meow', colors: [ 'black', 'orange' ] }
console.log(animals.getCatVoice(animals.cat));
// meowAs you can see in these examples, all the objects are imported in index.mjs from animals.mjs module by using the * operator. Using the as keyword, the module now has the name – animals, which helps you use all exported objects from the animals module.
If a module in ESM has a default export, and the * operator is used to import all of its exports then the default export can be accessed using the name default.
Using ECMAScript modules
One of the advantages of ES modules is that they can be used both on the frontend as well as the backend. On the frontend, you can use ES modules by adding a script tag with the type attribute set to "module" and pointing to the module file using the src attribute. For example:
<script type="module" src="index.js"></script>On the backend, NodeJS also supports ES modules since version 12. In order to use ES modules in NodeJS, you need to set the "type" property in the "package.json" file to "module". You can do that by using this line of code:
{ "type": "module" }Conclusion
ES modules are a native technology in ECMAScript that you can use to reuse and divide code. JavaScript is an implementation of ECMAScript, so you can use ESM in your projects on both the backend and the frontend. ESM can be a better way to create modern applications, because of native technologies that are supported by a large community of developers. Many new frameworks and JS libraries use ESM, and now you can use it too!