0%

javascript-import

Static import

The static import declaration is used to import read-only live bindings which are exported by another module.

Static import is syntactic.

Named imports

Each import has a name, which must be the same as the corresponding export name in the imported module.

1
2
3
4
5
6
7
8
// math.mjs
export function add(a, b) {
return a + b;
}

export function subtract(a, b) {
return a - b;
}
1
2
// main.mjs
import { add, subtract } from './math.mjs';

Default imports

You can import the default export of a module using the following syntax:

1
2
3
4
// math.mjs
export default function add(a, b) {
return a + b;
}

You can use any name here, not limited to add. But you can only have one default export per module.

1
2
// main.mjs
import add from './math.mjs';

Namespace imports

You can also import all exports from a module using the following syntax:

1
2
3
4
5
6
7
8
// math.mjs
export function add(a, b) {
return a + b;
}

export function subtract(a, b) {
return a - b;
}
1
2
// main.mjs
import * as math from './math.mjs';

Side effects imports

You can also import a module for its side effects only, without importing any bindings. This is useful when you just want to run the code in the module, but don’t need to import any of its bindings.

1
import './math.mjs';

This is very common in Angular code, especially with Module Federation, where you import a module for its side effects only.

1
2
3
4
5
6
7
// bootstrap.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));

main.ts use side effect import here which will run all global code in bootstrap.ts but not import any bindings. (Note, the following code is dynamic import, not static import)

1
2
3
// main.ts
import('./bootstrap')
.catch(err => console.error(err));

Static import only execute once

No matter how many times you import a module, it will only be executed once. Suppose you have a math module with the following code:

1
2
3
4
5
6
7
8
9
10
11
// math.mjs
export function addFunc(a, b) {
return a + b;
}

export function subtractFunc(a, b) {
return a - b;
}

// Print a random number to identify different instances
console.log(Math.random());

And another two module add and subtract with the following code:

1
2
3
4
5
6
// add.mjs
import { addFunc } from './math.mjs';

export function add(a, b) {
return addFunc(a, b);
}
1
2
3
4
5
6
// subtract.mjs
import { subtractFunc } from './math.mjs';

export function subtract(a, b) {
return subtractFunc(a, b);
}

and finlay, you import add and subtract in your main module:

1
2
3
4
5
6
// main.mjs
import { add } from './add.mjs';
import { subtract } from './subtract.mjs';

console.log(add(1, 2)); // 3
console.log(subtract(2, 1)); // 1

When you run main.mjs, you will see only one random number printed in the console, which means the math module is only executed once.

1
2
3
0.2534933886729216
3
1

Dynamic import

Introduction

import() is dynamic import, it returns a promise. The import() syntax, commonly called dynamic import, is a function-like expression that allows loading an ECMAScript module asynchronously and dynamically into a potentially non-module environment.

Basic example

1
2
3
4
5
6
7
8
9
10
import("ramda").then(module => {
const moduleDefault = module.default;
console.log(moduleDefault);
});

import("./utility.js").then(module => {
const DefaultFunction = module.default;
const exportFunction = module.exportFunction;
console.log(DefaultFunction, exportFunction);
});

With dynamic import, you can import modules conditionally. The following code load the chat-box when user click on the contact button.

1
2
3
4
5
6
const contactBtn = document.querySelector("#button");
contactBtn.addEventListener("click", () => {
import("chat-box").then(module => {
module.load(); // or perform any desired action
});
});

Async/Await

Since import() returns a promise, you can use async/await to import modules.

1
2
3
4
5
const contactBtn = document.querySelector("#button");
contactBtn.addEventListener("click", async () => {
const module = await import("chat-box");
module.load();
});

Destruction

You can also use object destruction to import modules.

1
2
3
4
5
const contactBtn = document.querySelector("#button");
contactBtn.addEventListener("click", async () => {
const { load } = await import("chat-box");
load();
});

Error handling

And last, don’t forget to handle error when importing modules.

1
2
3
4
5
try {
const module = await import("chat-box");
} catch(e) {
console.error(e)
}

Use import() in non-module environment

import() can be used in non-module environment, such as in a .js file or script tag without type="module".

1
2
3
4
// test.js, you don't need test.mjs here.
import("chat-box").then(module => {
module.load();
});
1
2
3
4
5
6
<!-- You don't need type="module" in script tag-->
<script>
import("chat-box").then(module => {
module.load();
});
</script>

But static import can’t be used in non-module environment, you will got Error: SyntaxError: Cannot use import statement outside a module, to make static import work, you must use file end with .mjs or add type="module" in script tag.

1
2
import {load} from "./chat-box.mjs";
load();
1
2
3
4
5
<!-- You need type="module" in script tag-->
<script type="module">
import {load} from "./chat-box.mjs";
load();
</script>

pass by reference

There is no real pass by reference in JavaScript like in C/C++ language, but you can use import to achieve similar effect.

First, create a module module.mjs, in this file, we export an object person.

1
2
3
4
5
// module.mjs
export const person = {
name: 'zdd',
age: 18
};

Create another module module1.mjs, in this file, we import person from module.mjs and change the age to 40.

1
2
3
// module1.mjs
import { person } from './module.mjs';
person.age = 40;

Finally, import module1.mjs and module.mjs in main.mjs, and print the person object, you will found the age is 40 now.

1
2
3
4
5
// main.mjs
import './module1.mjs'; // This will execute the code in module1.mjs
import { person } from './module.mjs';

console.log(person);

输出如下:

1
{ name: 'zdd', age: 40 }

import.meta.url

import.meta.url The full URL to the module, includes query parameters and/or hash (following the ? or #). In browsers, this is either the URL from which the script was obtained (for external scripts), or the URL of the containing document (for inline scripts). In Node.js, this is the file path (including the file:// protocol).

In Node.js, import.meta.url is the file path of the current module. It is similar to __filename in CommonJS modules.

This is server.ts file generated by angular SSR, you can see import.meta.url is used to get the current file path.

1
2
3
4
5
6
7
8
9
10
11
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');
}

References