What's the fuss about ES modules ?
August 01, 2020
Hello everybody, I hope you all are doing great 😄 It’s been more than a year since I published my last blog.
What motivated me to write this blog ?
Today’s topic is about the ES modules. Modules in JS have always confused me. I don’t know if its confusion or curiosity. After going through quite a bit of blogs as well as storm of tweets by few of my favorite JS authors, the topic of ES Modules confused me further. Most of the confusions were caused because of these common expressions summarized below -
- “modules are not supported in browser yet 😩”
- “all major browsers have enabled support for ES modules, yay !”
- “You could not use modules in browsers directly until ES modules came in”
- “Modules were always present in Node Js, but not on browser”
Whats so puzzling about these remarks ? Let me give you my perspective.
- I have coded a lot in React. To be honest, I was first introduced to Javascript via React. So my first question was - “I am using
export/import
throughout my code base, but still these blog posts suggests that modules does not exist in browser. Can’t i useexport/import
in browser directly ?” - I have been coding in Node.js for time now. So “Why do we use
export/import
in React &require/module.exports
in Node Js. Shouldn’t they follow the same format ?” - Out of curiosity once, I went ahead and coded two very simple JS files and used
import/export
in them. I added JS files to HTML and ran them normally and guess what happened ? NOTHING. I was like - ”What the hell, Shouldn’t that have worked?”. And to be honest with you guys, this was one of the major reasons for writing this blog. So lets start !!!!
This blog hope to answer above questions in detail. As a side note, this blog post focusses more on the browser side of modules. I will avoid going deep into Node Js, but will give you a brief idea wherever necessary. I will write a separate blog post about Modules in Node JS in future.
Lets take a brief look at what are the problems solved by modules ? and why do we need them in the first place ?
What problems does JS module solve ?
-
Lets say we have
50 lines
of JS code with us. We want to use that code. How to use it ?- Simple ! Write your JS inside the script tag of HTML.
<script> document.getElementById("demo").innerHTML = "Hello JavaScript!"; </script>
-
Now your code increased from
50 lines
to200 lines
, should keep writing the code in the same way ?-
Yes, we could definitely do that, but adding JS code in HTML file will bloat it and make it huge, seemingly impossible to read. So what we do ? We copy our JS code in new
index.js
file and include that file in our HTML<script src="index.js"></script>
-
-
Now, lets say you have a friend who wrote some helper code for you. How would you use it ? Lets say, that file has only one giant function with
500 lines
of code. How would you use that code ?-
One very simple way would be to copy the
500 lines
of code and put it in yourindex.js
file. This is okay in the current case, what if all 10 of your friends have written some code for you, would you copy the code in yourindex.js
for all 10 of your friends? You could, but it causes of a couple of problems.- Your
index.js
will become so huge that it will become impossible to read and debug - Lets say for simplicity that you overcome some problem somehow, how would you allow multiple developers contribute to code base ? They would necessary have to deal with the code they did not even write.
- Your
- Another way would be to create a separate file
helper.js
and include bothindex.js
as well ashelper.js
in your HTML file in order.helper.js
will set thehelperFn
in the global scope, and thenindex.js
would use that function accessing the global scope.
// index.html <script src="helper.js"></script> <script src="index.js"></script> // helper.js function helperFun() { console.log('hello world'); } window.helperFun = helperFun; // index.js helperFun() // 'hello world'
Will the above code work ? Hell yes ! Does this method have any problems ? Hell Yes !
- Order of script is important - Lets say we reverse the order of scripts of the above example. We put
index.js
first and thenhelper.js
. Then your code inindex.js
which uses function set byhelper.js
is not present. This might not seem to be a problem right now, but it is a problem when you have 50 JS files all depending on each other. - Makes the code hard to maintain - A new developer joining your team have to spend decent amount of extra time to figure the chaining of scripts highlighted above.
-
Scope pollution - Global scope is polluted. If you dont know about scopes, read this - https://scotch.io/tutorials/understanding-scope-in-javascript. Scope allows you to share code amongst scripts (one script will add the variable to the script and other would use it). This leads to couple of problems
- Some of the scripts have access to global variable which they dont even need in the first place. To give you example, lets say we have 3 script files -
index1.js
,index2.js
,index3.js;
index2.js
uses some helper function fromindex1.js
. Shouldindex3.js
have access to the helper function that was added byindex1.js
whenindex3.js
is not even using it ? - Following the example above, likewise
index3.js
could just come and alter the helper function added byindex1.js
- Some of the scripts have access to global variable which they dont even need in the first place. To give you example, lets say we have 3 script files -
-
Although there are different patterns that try to address these problems (more about them in later posts). But they are not something that were provided by JS intrinsically. So,
How do we solve above problems ?
We would want to have a way that would allow us to achieve something usable while avoiding these problems. Could you tell what is the solution - 3…2…1, you guessed it right Modules
With ECMAScript 2015, modules were introduced. And for years to come the modern browsers like Chrome and Firefox started adding support for the module system. To this date not all browsers support this system. ****The list of browsers that support modules can be found here
I am pretty sure, some of you might have a few questions in your minds
- 2015 ? Seriously ? What took TC39 so long to release this feature ?
- Even till now, not all browsers have support for modules ? What is taking so long to implement module systems in ALL the browsers ?
Continuing from the previous discussion, a few more questions to ponder over are
- If you have had some experience in React with Webpack or Parcel, you have been using
import/export
for quite some time. What do they do under the hood? - Node Js uses
require/module.exports
statements, but browser don’t even know what they are. Where does that come from and what is so special about them ?
Lets tackle these questions one by one. I will reorder these questions to give you a clear understanding.
-
Node Js uses
require/module.exports
statements, but browser don’t even know what they are. Where does that come from and what is so special about them ?- The first version of
Node.js 0.1.14
was launched in2011-08-26
and modules systems were part of it. Head to this link, and do a text search formodules
. This means that modules were introduced pretty early on in Node.js and has been there for the long time. -
The loading of modules is synchronous. What does that mean ? Let me give you and example.
// index.js const hello = require('hello.js'); const helper = require('helper.js'); console.log('hello index.js') ---------------------------------------------- // hello.js module.exports = function() { console.log('hello function'); } ---------------------------------------------- // helper.js module.exports = function() { console.log('helper function'); } ---------------------------------------------- // Execute - 'node index.js' -------------CONSOLE_OUTPUT------------------ 'hello function' 'helper function' 'hello index.js'
- Lets try to go through above code and execute execute
index.js
from the perspective of Node.js compiler. Node.js complier sees the firstrequire('hello.js')
statement, it goes on and fetches, loads & runs thehello.js
as a module. Once that file is loaded, the Node.js compiler moves on the secondrequire('helper.js')
statement, it goes, fetches and runshelper.js
as a module. - Now the question to ask is, can this strategy work in browsers ? Ummm…NO. Why ? Because if a module is loaded in this fashion in browser, the page would just STOP for every script to load. Hence we cannot use this approach.
- The first version of
-
If you have had some experience in React with Webpack or Parcel, you have been using
import/export
for quite some time. What do they do ?- Webpack & Parcel are called module bundlers.
- They do a very simple task - they take your modules that you created using import/export copies their source codes and pastes them SMARTLY in one single JS file.
-
There is a reason the word SMARTLY is highlighted, because this Webpack perform great optimizations before pasting your code. Few optimizations are listed below
- Minification - which just means as the name suggests, minifies the code of the modules. How ? A basic way would be to remove spaces in the source code, changing the large names of the class names etc.
- Tree Shaking - removes the unused code
-
2015 ? Seriously ? What took TC39 so long to release this feature ?
- There are many stakeholders and many different platforms involved (Node.js, npm, browsers, JS engines, TypeScript, TC39, etc.). That means, ES modules should be compatible with huge number of platforms. Lets take an example of Node.js, Node.js already has module system implemented, we dont wanna break them.
- As you know, getting alignment from these many stake holders takes time decent amount of times
-
What is taking so long to implement module systems in ALL the browsers ?
- Different browsers/platforms use different technologies under the hood - Chrome use V8, Firefox uses SpiderMonkey. All of them have to integrate ES modules without breaking previous functionalities.
Phewww!!! That was a lot, right ? I think that’s it for the blog. I hope you might have learned one or two new things today.
Bye Bye 👋👋👋👋 See you in 2 weeks !!