Modules

Early, libraries such RequireJS, Angular’s dependency injection mechanism and CommonJS are used for implement modularity. Since ES6 JavaScript has built-in support of modules.

The javascript code file can be used as a module. It is recommended that such files have the extension .mjs, but not all http servers support this. In this case, the .js extension is used.

Modules work only via HTTP(s), not in local files. If you try to open a web-page locally, you’ll find that import/export directives don’t work. You can solve this problem by running local http server with Node.js

Module scripts and their imports are fetched with CORS, so http-server must return valid CORS headers such as Access-Control-Allow-Origin: *.

// mymodule.mjs
export function myFunction(param) {...}
export function myFunction2(param) {...}
var data1="";
var data2="";
export default data1;

<script type="module"> import {myFunction} from './mymodule.mjs'; import {myFunction2} from 'https://mydomen.com/mjs/mymodule.mjs'; myFunction(123); </script>

export

The export keyword indicates that name can be used outside the module. All other names will be private for the module.

The export default allows define default name that will be imported from module. Only one default per file is allowed. This is useful when you are working with a large library such as jQuery and just want to import the entire library.

// ./mymodule.mjs file
export default function(x) {
  return x * x * x;
}
import cube from './mymodule.mjs'; var c3 = cube(2);

It is recommended to export at the end of the file with a list of all exported names.

// my module
function foo(){}
class Bar {}
var obj = {};
...
export { foo, Bar, obj };

import

The import keyword allows import name from other module. Valid module specifiers:

  • a full non-relative URL as "http://example.com/mjs/module.mjs"
  • starts with / - path from root
  • starts with ./ - path relative to the current path
  • starts with ../ - path relative to the parent path

You can import a single name or list of names. You can also override imported names.

import {nam1, name2 as myCoolName} from './mymodule.js';

* allows import all names.

import * as myModule from './mymodule.js';
myModule.func1();

If you want to include and execute code inside a js file without exporting, use the following syntax.

import "./mymodule.js";

There is dynamic form of import, that looks like function. It allows load module asynchronously by demand in regular script (without type="module").

<script type="module">
  (async () => {
    const {func1, func2} = await import('./lib.mjs');
    func1();
    func2();
  })();
</script>

browser supporting

All modern browsers support modules. Just set attribute type of script tag to "module".

Unlike regular scripts, modules are executed only once.
Modules can be preloaded.

Modules can be loaded with async attribute. Such modules will be executed as soon as its imports have fetched.

Inline module scripts are always deferred, whether they import anything or not.

<link rel="modulepreload" href="lib1.mjs">
<link rel="modulepreload" href="main.mjs">
<script type="module" src="main.mjs"></script>
<script nomodule src="fallback.js"></script>

<!-- inline module -->
<script async type="module">
  import {myFunction} from './mymodule.mjs';

  myFunction(123);
</script>

<!-- external module -->
<script async type="module" src="lib2.mjs"></script>