If we put export
in front of a named entity inside a module, it becomes a named export of that module. All other entities are private to the module.
//===== lib1.mjs =====
// Named exports
export const one = 1, two = 2;
export function myFunc() {
return 3;
}
//===== main1a.mjs =====
// Named imports
import {one, myFunc as f} from './lib1.mjs';
assert.equal(one, 1);
assert.equal(f(), 3);
// Namespace import
import * as lib1 from './lib1.mjs';
assert.equal(lib1.one, 1);
assert.equal(lib1.myFunc(), 3);
The string after from
is called a module specifier. It identifies from which module we want to import.
So far, all imports we have seen were static, with the following constraints:
- They have to appear at the top level of a module.
- The module specifier is fixed.
Dynamic imports via import()
[ES2020] don’t have those constraints:
//===== main1b.mjs =====
function importLibrary(moduleSpecifier) {
import(moduleSpecifier)
.then((lib1) => {
assert.equal(lib1.one, 1);
assert.equal(lib1.myFunc(), 3);
});
}
importLibrary('./lib1.mjs');
A default export is mainly used when a module only contains a single entity (even though it can be combined with named exports).
//===== lib2a.mjs =====
export default function getHello() {
return 'hello';
}
A default export is the exception to the rule that function declarations always have names. In the previous example, we can omit the name getHello
.
//===== lib2b.mjs =====
export default 123; // (A) instead of `const`
There can be at most one default export. That’s why const
or let
can’t be default-exported (line A).
//===== main2.mjs =====
import lib2a from './lib2a.mjs';
assert.equal(lib2a(), 'hello');
import lib2b from './lib2b.mjs';
assert.equal(lib2b, 123);
There are three kinds of module specifiers:
-
Absolute specifiers are full URLs – for example:
'https://www.unpkg.com/browse/[email protected]/browser.mjs' 'file:///opt/nodejs/config.mjs'
Absolute specifiers are mostly used to access libraries that are directly hosted on the web.
-
Relative specifiers are relative URLs (starting with
'/'
,'./'
or'../'
) – for example:./sibling-module.js ../module-in-parent-dir.mjs ../../dir/other-module.js
Relative specifiers are mostly used to access other modules within the same code base.
-
Bare specifiers are paths (without protocol and domain) that start with neither slashes nor dots – for example:
'some-package' '@some-npm-scope/some-scoped-package' 'some-package/with/sub-path'
Bare specifiers are mostly used to refer to npm packages.