Understanding ES Modules in Javascript
How to use ES Modules to import Javascript modules statically or dynamically.
Jun 11 ·8min read
Modules are found in every programming language. It is a way to include functionality in one code with another. These modules are where a developer can create code with specific functionality that can be reused elsewhere. Modules provide you with benefits such as code reusability and code that can be split into several chunks.
If you had been working with JavaScript before sometimes back, you would have come to know that JavaScript did not have modules as such. Developers resorted to using HTML <script>
tags in order to load JavaScript files into their applications. Later on, several module definition formats started to surface.
-
CommonJS — the
module.exports
andrequire
syntax used in Node.js - Asynchronous Module Definition (AMD)
- Universal Module Definition (UMD)
- ES Modules
Let us see why modules are needed in the first place.
Why Do We Need Modules
When you think about how programs work, all they do is manage variables. They assign values to variables, modify them, combine two variables together, etc. But when the number of variables increases with the size of your application, it can be cumbersome to manage them and maintain your code.
The solution to this was to have only a few variables that you should be worried about. JavaScript's way of achieving this was called scope . Because of how scopes work in JavaScript, functions can’t access variables that are defined in other functions.
Although this allows your variables to be inaccessible by other functions, it gave rise to another issue — it was hard for you to share variables between different functions. A common way of overcoming this and sharing variables out of scope was to put them on a scope above such as the global scope.
Although this approach worked, it came with problems. Your script tags should be in the correct order and you must make sure that no one changes that order. If the order does change, your app will throw an error. This made code management tricky. You never know what might break what. Any function can grab anything on the global, so you do not know which functions depend on which scripts.
Another issue was that every part of the code that’s inside of that global scope can change the variable. This would allow malicious and non-malicious code to access and even modify your global variables with or without malicious intent.
And then modules were introduced to help overcome these issues.
How Do Modules Make Things Better
Modules allow you to organize and manage variables and functions better. Usually, functions and variables that are apart of the same functionality are put together in a module. This puts these variables into the module scope. The module scope can be used to share variables between the functions in the module.
This also allows for variables to be available for other modules as well. They can explicitly say which variable, class or function should be available to outsider modules. This is called export . Once you have an export, other modules can explicitly say that they depend on that variable, class or function. Due to this explicit relationship, you will know which modules will break if you remove one.
Once you are able to import and export variables and functions, it is easier for you to split and break up your code into chunks of code that can work independently. You can later build your application by using these modules, similar to building with Lego blocks.
In order to achieve this super useful feature, there have been multiple attempts to add module functionality with JavaScript.
Existing Module Systems
CommonJS
CommonJS is what has been used in NodeJS historically. With Node, you get CommonJS module.exports
and require
out of the box. However, unlike Node, the browser doesn’t support CommonJS. Moreover, CommonJS loads modules synchronously and therefore it is not an optimal solution for browsers. You can use bundlers such as Webpack or Browserify to overcome this problem.
// filename: bar.js // dependencies var $ = require('jquery'); // methods function myFunction(){}; // exposed public method (single) module.exports = myFunction;
Asynchronous Module Definition (AMD)
AMD was born out of a group of developers who did not like the direction of CommonJS. In fact, AMD was split from CommonJS early in its development. The main difference between AMD and CommonJS is that AMD loads modules asynchronously. This was very much popular in browsers as startup times are essential to good user experience.
// filename: bar.jsdefine(['jquery'], function ($) { // methods function myFunction(){}; // exposed public methods return myFunction; });
Universal Module Definition (UMD)
As CommonJS and AMD were quite popular in their respective domains, there was a need for a “universal” pattern that supports both styles. But as it turns out, UMD was messy and ugly. Although it did support both AMD and CommonJS, as well as supporting the old-style “global” variable definition.
What are ES Modules
As you can clearly see, JavaScript lacked one proper standard module definition format. Hence a single, native module standard was therefore proposed in ES6.
The static nature of the import
and export
directive allows static analyzers to build a full tree of dependencies without running code.
The result is syntactically pleasing and compatible with both synchronous and asynchronous modes of operation in the browser. ES modules have quickly become available in the browser, but in Node.js it was a bit harder to come up with a solution that is backward-compatible and enables incremental upgrades. In Node.js native ES modules are available behind the experimental-modules flag for a long time.
The following is an example for ES6 modules.
JavaScript
//------ library.js ------ export const sqrt = Math.sqrt; export function square(x) { return x * x; } export function diagonal(x, y) { return sqrt(square(x) + square(y)); }//------ main.js ------ import { square, diagonal } from 'library'; console.log(square(13)); // 169 console.log(diagonal(12, 5)); // 13 const app = document.getElementById("app"); app.innerHTML = "<h1>Demo App for ES Modules</h1>";const input = document.getElementById("num"); input.addEventListener("change",displaySquare);function displaySquare(){ var sqrOutput = document.getElementById("sqr"); sqrOutput.value = square(input.value); }
HTML
<HTML>
<head>
<title>ES Modules Demo</title>
</head>
<body>
<script type="module" src="./main.js" ></script>
<div id="app"></div>
<label>Input</label>
<input id="num" type="number" placeholder="Enter number here"/>
<br>
<label>Output</label>
<input id="sqr" type="number" disabled/>
</body>
</HTML>
As you can see above in the HTML file, you need to specify type="module"
in the script tag for the browser to treat it as an ECMAScript module.
Backwards Compatibility
For backwards compatibility, you can includenomodule
in the script tag (where the JS file loaded is single bundled file). Browsers with ES Modules support will know to ignore that. This solution will work even in the oldest of browsers. Willem’s answer
has explained this very well. In the above scenario, we will include something like this in our HTML.
<script type="module" src="./main.js" >
</script>
<script nomodule src="./fallback.js" >
</script>
Note:
.
Modules are imported with either absolute or Relative references and must start with either “/”, “./”, or “../”.
Dynamic Imports
The latest ES2020 version does come with dynamic imports
. To dynamically import modules, the import
keyword may be called as a function. When used this way, it returns a promise.
import('/modules/library.js') .then((module) => { // Do something with the module. });//or using await let module = await import('/modules/library.js');
ES Modules Browser Compatibility
The use of native modules in the browser depends on the import
and export
statements, the browser compatibility of which is as follows.
Should You Choose ES Modules?
For browsers, ES modules are the new standard. With asynchronous module loading out of the box, you can expect faster startup times for better performance. Although you can use CommonJS with some additional plugins in the browser, it is highly advised that you switch to ES modules as they are native in browsers.
ES native modules allow you to get individual modules loaded rather than a single bundle file. This is quite useful and it reduces the size of the data loaded. Browser compatibility for native modules is also important as it decides whether native ES modules will be implemented or whether we will fallback to our module bundler which will load a single bundle file.
One of the issues, when you get a single bundle file, is that when your applications become bigger, the size of the bundle js file will also increase and thereby affecting startup time and performance. You can avoid this by using code splitting which is a feature available in modern bundlers such as webpack.
But there might be instances where we would opt for a module bundler such as webpack over ES modules. If you have assets such as CSS, images, fonts and even data files such as XML, CSV, you might want to opt for a webpack solution as webpack provides asset bundling.
You also should take into account the support for HTTP2 in browsers. When you use native modules, your browser will load those modules individually. But rather than sending several HTTP requests, with the help of HTTP2, we can serve multiple requests simultaneously with a single connection. 96.49% of browsers use HTTP2 according to CanIUse .
But when you are developing an application that should cater even the remaining 3.51%, then you might want to switch to webpack. This is because your application would need to send several HTTP requests to load each individual modules if you stick with native ES modules.
In Node, things are quite different. As the feature is still flagged as experimental, it is better that you stick with CommonJS. You can still give ES modules a try though .
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。