Use Modules to Build a Modular JavaScript App
Tags: webdev, javascript, tutorial, beginners
Published: 2020-01-12 00:13:31

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

**Follow me on Twitter at https://twitter.com/AuMayeung**

**Many more articles at https://medium.com/@hohanga**

One of the big features of ES6 is JavaScript supporting built-in modules. Modules allow us to share code between files by using the `export` and `import` syntax. This is a big improvement over using `script` tags and global variables to share code across files.

Using `script` tags was error-prone since the load order matters. Having scripts in the wrong order could cause our program to execute code that hasn’t been declared yet. It also forced us to write spaghetti code with no real structure or logical composition. This problem doesn’t exist with modules because everything is exported and imported directly between files. Also, we can know the definition of the imported code easily since it’s explicit in which modules are being imported and referenced.

Exports and Imports
===================

To make code in a JavaScript file importable, we have to export them explicitly with the `export` statement. To do this, we just put `export` in front of the variable or constant you want to expose to other files. For example, we can write:

```javascript
export let num = 1;
```

This exports the `num` variable so that other modules can `import` it and use it.

We can export anything declared with `var`, `let`, `const`, and also functions and classes. The items exported must be declared at the top level. `export` cannot be used anywhere else, like inside functions and classes.

We can export multiple members at once. All we have to do is wrap all the members in curly brackets separated by commas. For example, we can write:

```javascript
const num1 = 1;  
const num2 = 2;  
const num3 = 3;  
export {num1, num2, num3};
```

This will let us import `num1`, `num2`, and `num3` in other JavaScript files.

Now that we have exported the members, we can import them in other JavaScript files. We can use the `import` statement to import one or more members into a module and work with them. For example, if we have the following in `moduleA.js`:

```javascript
const num1 = 1;  
const num2 = 2;  
const num3 = 3;  
export {num1, num2, num3};
```

Then in `moduleB.js` we can write the following code to import the items from `moduleA.js`:

```javascript
import {num1, num2, num3} from './moduleA'
```

The path after the `from` keyword starts with a period. The period means that we’re in the current folder.

This assumes that `moduleA.js` and `moduleB.js` are in the same folder. If we have them in different folder, then we have the specify the path of `moduleA.js` relative to `moduleB.js` if we want to import the exported members of `moduleA.js` into `moduleB.js`. For example, if `moduleA.js` is one level above `moduleB.js`, then in `moduleB.js` we write:

```javascript
import {num1, num2, num3} from '../moduleAFolder/moduleA'
```

The 2 periods before the path means that we go up one folder level and then get the `moduleAFolder` and then get `moduleA.js`.

We can also use JavaScript modules in `script` tags. To do this, we must set the `type` attribute of the script tag to `module` to use them. For example, if we want to use `moduleA.js` in our HTML file, we can write:

```html
<script type='module' src='moduleA.js'></script>
```

We can use `import` and `export` in JavaScript modules. They won’t work with regular scripts.

Scripts run in strict mode automatically, so we can’t accidentally declare global variables and do other things that can be done without strict mode being enabled. They also load asynchronously automatically so that we won’t have to worry about long scripts holding up a page from loading. Also, `import` and `export` only happens between 2 scripts, so no global variables are set. Therefore, they can’t be viewed in the console directly.

Default Exports
===============

There’s also a default export option for exporting a module’s members. We previously exported the variable in a way where we import them by the name. There’s also the default export that exports a single member from a module without needing to reference it explicitly by name when you’re importing it. For example, if we have a single member in a module that we want to export, we can write the following in `moduleA.js`:

```javascript
const num = 1;  
export default num;
```

There are no curly braces when you use `export default`.

Then in the file where you want to import the member. In `moduleB.js` we write:

```javascript
import num from './moduleA'
```

Once again, we omit the curly braces. This is because only one default export is allowed per module. Alternatively, we can write the following in `moduleB.js` :

```javascript
import {default as num} from './moduleA'
```

Renaming Imports and Exports
============================

If we have many modules and their exported members have the same name, then there will be conflicts if we try to import multiple modules. This is will be a problem that we need to fix. Fortunately, JavaScript has the `as` keyword to let us rename exports and imports so we can avoid name conflicts in our code. To use the `as` keyword to rename exports, we write the following in `moduleA.js`:

```javascript
export {  
  num1 as numOne,  
  num2 as numTwo  
}
```

Then in `moduleB.js`, we can import them by writing:

```javascript
import { numOne, numTwo } from './moduleA'
```

Alternatively, we can do the renaming when we import instead. To do this, in `moduleA.js`, we put:

```javascript
export {  
  num1,  
  num2  
}
```

Then in `moduleB.js`, we put:

```javascript
import { num1 as numOne, num2 as numTwo } from './moduleA'
```

If we try to import members with modules where the members have the same name, like:

```javascript
import { num1, num2 } from './moduleA';  
import { num1, num2 } from './moduleB';  
import { num1, num2 } from './moduleC';
```

We will see that we get `SyntaxError`. So we have to rename them so that the module will run:

```javascript
import { num1 as num1A, num2 as num2A } from './moduleA';  
import { num1 as num1B, num2 as num2B } from './moduleB';  
import { num1 as num1C, num2 as num2C } from './moduleC';
```

A cleaner way to import from multiple modules that have members with the same names is to import all of the module’s exported members as one object. We can do that with an asterisk. For example, instead of:

```javascript
import { num1 as num1A, num2 as num2A } from './moduleA';  
import { num1 as num1B, num2 as num2B } from './moduleB';  
import { num1 as num1C, num2 as num2C } from './moduleC';
```

We can instead write:

```javascript
import * as moduleA from './moduleA';  
import * as moduleB from './moduleB';  
import * as moduleC from './moduleC';
```

Then in code below the imports, we can write:

```javascript
moduleA.num1;  
moduleA.num2;  
moduleB.num1;  
moduleB.num2;  
moduleC.num1;  
moduleC.num2;
```

We can also export and import classes. So if we have a file that contains one or more classes, like the `Person.js` file with the class below:

```javascript
class Person {  
  constructor(firstName, lastName) {  
    this._firstName = firstName;  
    this._lastName = lastName;  
  }  
  get fullName() {  
    return `${this.firstName} ${this.lastName}`  
  }  
  get firstName() {  
    return this._firstName  
  }  
  get lastName() {  
    return this._lastName  
  }  
  sayHi() {  
    return `Hi, ${this.firstName} ${this.lastName}`  
  }  
  set firstName(firstName) {  
    this._firstName = firstName;  
  }  
  set lastName(lastName) {  
    this._lastName = lastName;  
  }  
}
```

Then we write the following to export a class:

```javascript
export { Person };
```

This exports the `Person` class, and then to import it, we write:

```javascript
import { Person } from './person';
```

Dynamic Module Loading
======================

JavaScript modules can be loaded dynamically. This lets us only load modules when they’re needed rather than loading all of them when the app runs. To do this, we use the `import()` function, which returns a promise. When the module in the argument is loaded, then the promise is fulfilled. The promise resolves to a module object, which can then be used by the app’s code. If we have the `Person` class in `Person.js`, then we can dynamically import it with the following code:

```javascript
import('./Person')  
.then((module)=>{  
  const Person = module.Person;  
  const person = new Person('Jane', 'Smith');  
  person.sayHi();  
})
```

Or using the `async` and `await` syntax, we can put that in a function:

```javascript
const importPerson = async ()=>{   
  const module = await import('./Person');  
  const Person = module.Person;  
  const person = new Person('Jane', 'Smith');  
  person.sayHi();  
}

importPerson();
```

As you can see, JavaScript modules are very useful for organizing code. It allows us to export things that we want to expose to other modules, eliminating the need for global variables. In addition, exports and imports can be renamed to avoid conflict when importing multiple modules. Also, all the exported members can be imported all at once so that we get the whole module as an object instead of importing individual members. Finally, we can use `export default` if we only want to export one thing from our module.