If you already have a subscription, you can sign in.
Enjoy free content straight from your inbox 💌
00:00
One of the greatest features of modern JavaScript is a standardized module system that works for both browsers and server environments. Modules allow you to Keep your code organized and maintainable, and the key way you control How the module system Works in a JavaScript project is by Using the package such as file Within an empty folder, we create a new package to J and file, and we only need two fields in order to turn this folder into a package for JavaScript modules. The first field is type, which we need to set to module in order to let no JS or any frontend build tool know about the fact that the files within this package will consist of ES
00:37
modules. The next field is main, which must be a full relative path, including a file extension to our main application or package entry point. Modern project templates for popular frameworks already come with these options configured, but it's good to know what these options are doing. Let's demonstrate this in action by creating our main application entry point source index js. Because we've specified that we have type module, it means that we can use ES module syntax like a simple export statement. Of course, in addition to exports, we can add any of the JavaScript logic as well. For example,
01:11
here's a simple consult log. Now, in order to execute this package, all that we need to do is to go into the folder that contains package rotation and pass in dot for the current directory to no jss. And because everything ran without issues, it means our configuration is complete and validated With this simple setup out of the way. Let's take deeper into the core syntax for JavaScript ES modules and at its heart it consists of export and import statements. We create a simple file example JS and add a few simple JavaScript identifiers. We have a message pointing to a string,
01:45
a simple sum function that adds to values, a simple subfunction which subtract to values, and now we want to export them for reuse. Across our code base, we can export the things that we intend to use elsewhere by using an export statement, and at its simplest, we specify the identifiers. We want to export between two simple curly braces separated by commas. Now let's look at how we can use these exported identifiers in a different module. We can bring in all the exports of a module into a single identifier by using the import star syntax.
02:17
So here the identifier example will contain everything that was exported from example jss. We can use the identifier example to read the message property, which will be the string. Hello modules. We can use the sum subfunction to add numbers. We can use the subfunction to subtract numbers, and this is the fundamental pattern of code export and code import using ES modules. In addition to exporting and importing things as a whole in bulk, we can also export items individually at the point of declaration and similarly import items individually
02:49
By name. So let's get rid of the sum and the sub exports from our bulk export statement. Instead, we can prefix the function declarations or even the cons if you wanted to with the export keyword, and this will automatically add them to the exports. For this particular module, this code is functionally equivalent to the code that existed before we started this refactor. Similarly, on the import side, instead of bringing everything into the properties of a single identifier called example, we can bring in the different identifiers by name from the example module by using curly brackets in our import statement.
03:23
So in addition to example sum and example sub, we have the sum and sub functions directly imported as well, which means that we can use them without the example prefix. And functionally, this code is going to be exactly the same as before, and if you were not using example message, you could even get rid of the import star altogether. Now, it is understandable that the identifiers exported from example, jss might conflict with identifiers. We have within our index jss. For example, if you had a local sum or subfunction, we would run into name conflicts with import star, you are free to call the identifier whatever you want.
03:58
You don't have to call it example, but with named imports you need to use the name that was exported from example js. However, we can change the name that exists within index JSS by using as followed by the identifier that we want to use. So now some and sub are no longer identifiers within index or trace. And if you want to use them, we must use the identifiers, submission, and subtraction. And now if you open up the terminal and execute this code again, you can see it still works confirming our additional understanding of JavaScript modules. Quite commonly,
04:29
you only have a one main export from a file that you want people to import and to make this patent convenient. JavaScript comes with a special syntax for a default export and a default import. To specify that an export is a default export, we simply use export default. There can only be one default export within a module. And ha, we are saying that for the example module message is the default export. Bringing in a default export is one of the simplest things within JavaScript. We simply import followed by the identifier we want to use for the default within this module, followed by from, followed by the module.
05:05
And now we can use this identifier message anywhere within index js for example, we can modify our first statement to simply log out the message. The static import statements, which we have been using, need to be defined upfront. This means they need to be at the root of the file. The import name cannot be available and the import cannot be done conditionally. To demonstrate how these requirements can be a bit restrictive, let's create two simple TR modules. First we create a module circle js, and within that we simply log out circle js loaded and we provide a simple function called render, which logs to the console rendering circle.
05:42
Then we create another module square js, which follows a similar pattern. It logs to the console square js loaded and provides a simple function called render, which logs a rendering square to the console. Within our main application entry point point index js, we have a shapes array, which consists of a circle and a square and for each shape of shapes, if the shape is a circle, we want to import circle from circular js and then use its render method. And if the shape is a square, then we want to import square from square js and use the squares render method. Unfortunately,
06:14
this particular code is not going to execute and it'll immediately error out because an import declaration can only be used at the top level of a module, the fix is pretty easy. We need to take these import statements and move them out of any condition or function or a fall loop. And now if you were to execute this code, it would work without any issues. Circular chase will get loaded, square chase will get loaded, and we can use their methods to render circles or squares if you wanted to to allow us to dynamically load modules based on a condition transcript comes with a special promise based dynamic import syntax that allows loading of modules
06:51
using conditions and variables. Right now we are rendering a circle and a square using the circle and the square modules. But what if the shapes that the user wants to draw only consist of circles? You can see that we are only rendering circles over here, but we are still loading squared or js, even though this is not required for this particular input of shapes. Loading, passing, and executing JavaScript that we do not need is a waste of resources that we should try to avoid to allow us to conditionally and lazily import the modules that we need. JavaScript provides dynamic imports.
07:25
A dynamic import within JavaScript consists of the import keyword followed by parenthesis. This actually returns a promise for the module that we want to load and we can simply wait for it to resolve by using the AWAI keyword. And then same as before, we use the returned modules render method to render a circle. We can repeat the process if we ever encounter a square, we await and import for square js, store it into the square variable, and then use its render method. And now if you only have shapes that are circles, then only circle js will be loaded and we can verify that by executing this code.
07:59
If you modify the shapes to include square as well and execute the code again, then of course square js will also get loaded with dynamic modules. In addition to loading them conditionally, we can actually even use variables for the path of the module that we want to load and knowledge of this fact can actually simplify this particular piece of code significantly. We use the shape variable in each iteration to import the required module, and then from the returned object, you pick out the render method and then all that we need to do is to invoke this render method which will belong to that shape. Functionally,
08:33
this code is exactly as what we had before, but as you can see, it is much more concise and cleaner in its purpose. One thing that is often overlooked by D script modules is the fact that the module code is executed only once and on first request. After that, the exports get collected and GED and the same exports are returned. Every time the module is requested, we create a simple module that starts off by logging to the console module loading started, then exports a simple message variable, and then at the end of the module it logs out. Module loading completed. Now let's jump into our indexer js and import this module using a standard
09:09
static import. The first time within our program, we request this module from the JavaScript runtime. It'll look at its case and if this module has never been executed before, it'll execute the module code and then collect all of the exports that that module provides and return them as a response to our request. And now, anytime from anywhere within our program, if you request the same module using any syntax, for example, in Port Star or a dynamic import or anything else, we will always get that case result back from the JavaScript runtime and we can actually verify that if first is strictly equal to second,
09:43
and indeed there will be references to the same value in addition to first and second being equal because the result came back true. There are other things that you should notice in this console as well. Before the first was returned, the original module code started executing and then it completed. Then the first was returned, and then for the second case, the module code was not executed again, which once more points to the fact that we will return the value. The beauty of cript modules is that it allows people to freely publish and share reusable code using a central registry called N P M.
10:18
All that's needed in order to import these packages is a simple N P M install. For this demonstration, we will be using a very simple but very popular N P M package called joc, which has right now 251 million downloads every week to get joc. All that we need to do is to open up the terminal in the folder that contains our package to Jason and execute N P M I joc. Once this installation is complete, you can see that JOC is installed and loaded into node modules to bring in a module which is installed as a package. For the path of our import.
10:51
We actually use the package name. So here you can see that the import path is just chalk, not chalk js, and it is not a relative path like we have been using for our own modules. Now, the chalk module only has a default export, which is why we are using the default import syntax to bring that export into a chalk identifier, and now we can start using chalk to make our terminal logs prettier. For example, I love JavaScript in yellow and bold, and if you execute this code, that is exactly what we see in the terminal output. Once you understand JavaScript modules and start using them,
11:27
it fundamentally changes the level of things that you can achieve. As always, thank you for joining me and I will see you in the next one.