Skip to content

Commit d2b5012

Browse files
authored
Split dynamic import into its own page (#17402)
* Split dynamic import into its own page * address reviews * add an example * minor tweak
1 parent 39fe500 commit d2b5012

File tree

3 files changed

+178
-100
lines changed

3 files changed

+178
-100
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
title: import
3+
slug: Web/JavaScript/Reference/Operators/import
4+
tags:
5+
- ECMAScript 2015
6+
- JavaScript
7+
- Language feature
8+
- Modules
9+
- Reference
10+
- dynamic import
11+
- import
12+
browser-compat: javascript.operators.import
13+
---
14+
{{jsSidebar("Operators")}}
15+
16+
The `import()` call, commonly called _dynamic import_, is a function-like expression that allows loading an ECMAScript module asynchronously and dynamically into a potentially non-module environment.
17+
18+
Unlike the [declaration-style counterpart](/en-US/docs/Web/JavaScript/Reference/Statements/import), dynamic imports are only evaluated when needed, and permits greater syntactic flexibility.
19+
20+
## Syntax
21+
22+
```js
23+
import(moduleName)
24+
```
25+
26+
The `import()` call is a syntax that closely resembles a function call, but `import` itself is a keyword, not a function. You cannot alias it like `const myImport = import`, which will throw a {{jsxref("SyntaxError")}}.
27+
28+
### Parameters
29+
30+
- `moduleName`
31+
- : The module to import from. The evaluation of the specifier is host-specified, but always follows the same algorithm as static [import declarations](/en-US/docs/Web/JavaScript/Reference/Statements/import).
32+
33+
### Return value
34+
35+
It returns a promise which fulfills to an object containing all exports from `moduleName`, with the same shape as a namespace import (`import * as name from moduleName`): an object with `null` prototype, and the default export available as a key named `default`.
36+
37+
## Description
38+
39+
The import declaration syntax (`import something from "somewhere"`) is static and will always result in the imported module being evaluated at load time. Dynamic imports allows one to circumvent the syntactic rigidity of import declarations and load a module conditionally or on demand. The following are some reasons why you might need to use dynamic import:
40+
41+
- When importing statically significantly slows the loading of your code and there is a low likelihood that you will need the code you are importing, or you will not need it until a later time.
42+
- When importing statically significantly increases your program's memory usage and there is a low likelihood that you will need the code you are importing.
43+
- When the module you are importing does not exist at load time.
44+
- When the import specifier string needs to be constructed dynamically. (Static import only supports static specifiers.)
45+
- When the module being imported has side effects, and you do not want those side effects unless some condition is true. (It is recommended not to have any side effects in a module, but you sometimes cannot control this in your module dependencies.)
46+
- When you are in a non-module environment (for example, `eval` or a script file).
47+
48+
Use dynamic import only when necessary. The static form is preferable for loading initial dependencies, and can benefit more readily from static analysis tools and [tree shaking](/en-US/docs/Glossary/Tree_shaking).
49+
50+
If your file is not run as a module (if it's referenced in an HTML file, the script tag must have `type="module"`), you will not be able to use static import declarations, but the asynchronous dynamic import syntax will always be available, allowing you to import modules into non-module environments.
51+
52+
## Examples
53+
54+
### Import a module for its side effects only
55+
56+
```js
57+
(async () => {
58+
if (somethingIsTrue) {
59+
// import module for side effects
60+
await import("/modules/my-module.js");
61+
}
62+
})();
63+
```
64+
65+
If your project uses packages that export ESM, you can also import them for side
66+
effects only. This will run the code in the package entry point file (and any files it
67+
imports) only.
68+
69+
### Importing defaults
70+
71+
You need to destructure and rename the "default" key from the returned object.
72+
73+
```js
74+
(async () => {
75+
if (somethingIsTrue) {
76+
const {
77+
default: myDefault,
78+
foo,
79+
bar,
80+
} = await import("/modules/my-module.js");
81+
}
82+
})();
83+
```
84+
85+
### Importing on-demand in response to user action
86+
87+
This example shows how to load functionality on to a page based on a user action, in this case a button click, and then call a function within that module. This is not the only way to implement this functionality. The `import()` function also supports `await`.
88+
89+
```js
90+
const main = document.querySelector("main");
91+
for (const link of document.querySelectorAll("nav > a")) {
92+
link.addEventListener("click", (e) => {
93+
e.preventDefault();
94+
95+
import("/modules/my-module.js")
96+
.then((module) => {
97+
module.loadPageInto(main);
98+
})
99+
.catch((err) => {
100+
main.textContent = err.message;
101+
});
102+
});
103+
}
104+
```
105+
106+
### Importing different modules based on environment
107+
108+
In processes such as server-side rendering, you may need to load different logic on server or in browser because they interact with different globals or modules (for example, browser code has access to web APIs like `document` and `navigator`, while server code has access to the server file system). You can do so through a conditional dynamic import.
109+
110+
```js
111+
let myModule;
112+
113+
if (typeof window === "undefined") {
114+
myModule = await import("module-used-on-server");
115+
} else {
116+
myModule = await import("module-used-in-browser");
117+
}
118+
```
119+
120+
### Importing modules with a non-literal specifier
121+
122+
Dynamic imports allow any expression as the module specifier, not necessarily string literals.
123+
124+
Here, we load 10 modules: `/modules/module-0.js`, `/modules/module-1.js`... in parallel, and call the `load` functions that each one exports.
125+
126+
```js
127+
Promise.all(
128+
Array.from({ length: 10 }).map((_, index) =>
129+
import(`/modules/module-${index}.js`)
130+
)
131+
).then((modules) => modules.forEach((module) => module.load()));
132+
```
133+
134+
## Specifications
135+
136+
{{Specifications}}
137+
138+
## Browser compatibility
139+
140+
{{Compat}}
141+
142+
## See also
143+
144+
- [`import` declaration](/en-US/docs/Web/JavaScript/Reference/Statements/import)

files/en-us/web/javascript/reference/statements/export/index.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ browser-compat: javascript.statements.export
1313
---
1414
{{jsSidebar("Statements")}}
1515

16-
The **`export`** statement is used
16+
The **`export`** declaration is used
1717
when creating JavaScript modules to export live bindings to functions, objects, or
1818
primitive values from the module so they can be used by other programs with the
19-
{{jsxref("Statements/import", "import")}} statement. The value of an imported binding
19+
{{jsxref("Statements/import", "import")}} declaration. The value of an imported binding
2020
is subject to change in the module that exports it. When a module updates the value of
2121
a binding that it exports, the update will be visible in its imported value.
2222

@@ -42,6 +42,7 @@ export { name1, name2, …, nameN };
4242

4343
// Renaming exports
4444
export { variable1 as name1, variable2 as name2, …, nameN };
45+
export { variable1 as "string name" };
4546

4647
// Exporting destructured assignments with renaming
4748
export const { name1, name2: bar } = o;
@@ -62,8 +63,7 @@ export { default, … } from …;
6263
```
6364

6465
- `nameN`
65-
- : Identifier to be exported (so that it can be imported via
66-
{{jsxref("Statements/import", "import")}} in another script).
66+
- : Identifier to be exported (so that it can be imported via {{jsxref("Statements/import", "import")}} in another script). If you use an alias with `as`, the actual exported name can be specified as a string literal, which may not be a valid identifier.
6767

6868
## Description
6969

files/en-us/web/javascript/reference/statements/import/index.md

+30-96
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,15 @@ browser-compat: javascript.statements.import
1414
---
1515
{{jsSidebar("Statements")}}
1616

17-
The static **`import`** statement is
18-
used to import read-only live bindings which are [exported](/en-US/docs/Web/JavaScript/Reference/Statements/export) by
19-
another module.
17+
The static **`import`** declaration is used to import read-only live bindings which are [exported](/en-US/docs/Web/JavaScript/Reference/Statements/export) by another module.
2018

2119
Imported modules are in {{JSxRef("Strict_mode","strict mode")}}
2220
whether you declare them as such or not. The `import` statement cannot be
2321
used in embedded scripts unless such script has a `type="module"`. Bindings
2422
imported are called live bindings because they are updated by the module that exported
2523
the binding.
2624

27-
There is also a function-like dynamic **`import()`**, which
28-
does not require scripts of `type="module"`.
25+
There is also a function-like dynamic [`import()`](/en-US/docs/Web/JavaScript/Reference/Operators/import), which does not require scripts of `type="module"`.
2926

3027
Backward compatibility can be ensured using attribute `nomodule` on the
3128
{{HTMLElement("script")}} tag.
@@ -37,28 +34,25 @@ import defaultExport from "module-name";
3734
import * as name from "module-name";
3835
import { export1 } from "module-name";
3936
import { export1 as alias1 } from "module-name";
37+
import { default as alias } from "module-name";
4038
import { export1 , export2 } from "module-name";
4139
import { export1 , export2 as alias2 , [...] } from "module-name";
40+
import { "string name" as alias } from "module-name";
4241
import defaultExport, { export1 [ , [...] ] } from "module-name";
4342
import defaultExport, * as name from "module-name";
4443
import "module-name";
45-
var promise = import("module-name");
4644
```
4745

4846
- `defaultExport`
49-
- : Name that will refer to the default export from the module.
47+
- : Name that will refer to the default export from the module. Must be a valid JavaScript identifier.
5048
- `module-name`
51-
- : The module to import from. This is often a relative or absolute URL to the
52-
`.js` file containing the module. Certain bundlers may permit or require
53-
the use of the extension; check your environment. Only single quoted and double
54-
quoted Strings are allowed.
49+
- : The module to import from. The evaluation of the specifier is host-specified. This is often a relative or absolute URL to the `.js` file containing the module. In Node, extension-less imports often refer to packages in `node_modules`. Certain bundlers may permit importing files without extensions; check your environment. Only single quoted and double quoted Strings are allowed.
5550
- `name`
56-
- : Name of the module object that will be used as a kind of namespace when referring to
57-
the imports.
51+
- : Name of the module object that will be used as a kind of namespace when referring to the imports. Must be a valid JavaScript identifier.
5852
- `exportN`
59-
- : Name of the exports to be imported.
53+
- : Name of the exports to be imported. The name can be either an identifier or a string literal, depending on what `module-name` declares to export. If it is a string literal, it must be aliased to a valid identifier.
6054
- `aliasN`
61-
- : Names that will refer to the named imports.
55+
- : Names that will refer to the named imports. Must be a valid JavaScript identifier.
6256

6357
## Description
6458

@@ -67,6 +61,10 @@ as a kind of namespace to refer to the exports. The `export` parameters
6761
specify individual named exports, while the `import * as name` syntax imports
6862
all of them. Below are examples to clarify the syntax.
6963

64+
`import` declarations are only permitted at the top-level of modules, and can only be present in module files. If an `import` declaration is encountered in non-module contexts (for example, script files, `eval`, `new Function`, which all have "script" or "function" as parsing goals), a `SyntaxError` is thrown. To load modules in non-module contexts, use the [dynamic import](/en-US/docs/Web/JavaScript/Reference/Operators/import) syntax instead.
65+
66+
`import` declarations are designed to be syntactically rigid (for example, only string literal specifiers, only permitted at top-level, all bindings must be identifiers...), which allows modules to be statically analyzed and synchronously linked before getting evaluated. This is the key to making modules asynchronous by nature, powering features like [top-level await](/en-US/docs/Web/JavaScript/Guide/Modules#top_level_await).
67+
7068
### Import an entire module's contents
7169

7270
This inserts `myModule` into the current scope, containing all the exports
@@ -84,6 +82,8 @@ namespace. For example, if the module imported above includes an export
8482
myModule.doAllTheAmazingThings();
8583
```
8684

85+
`myModule` is an object with `null` prototype, and the default export will be available as a key called `default`.
86+
8787
### Import a single export from a module
8888

8989
Given an object or value named `myExport` which has been exported from the
@@ -114,6 +114,18 @@ import {reallyReallyLongModuleExportName as shortName}
114114
from '/modules/my-module.js';
115115
```
116116

117+
A module may also export a member as a string literal which is not a valid identifier, in which case you must alias it in order to use it in the current module.
118+
119+
```js
120+
// /modules/my-module.js
121+
const a = 1;
122+
export { a as "a-b" };
123+
```
124+
125+
```js
126+
import { "a-b" as a } from "/modules/my-module.js";
127+
```
128+
117129
### Rename multiple exports during import
118130

119131
Import multiple exports from a module with convenient aliases.
@@ -134,17 +146,6 @@ the module's global code, but doesn't actually import any values.
134146
import '/modules/my-module.js';
135147
```
136148

137-
This works with [dynamic imports](#dynamic_imports) as well:
138-
139-
```js
140-
(async () => {
141-
if (somethingIsTrue) {
142-
// import module for side effects
143-
await import('/modules/my-module.js');
144-
}
145-
})();
146-
```
147-
148149
If your project uses packages that export ESM, you can also import them for side
149150
effects only. This will run the code in the package entry point file (and any files it
150151
imports) only.
@@ -177,54 +178,10 @@ import myDefault, {foo, bar} from '/modules/my-module.js';
177178
// specific, named imports
178179
```
179180

180-
When importing a default export with [dynamic imports](#dynamic_imports), it
181-
works a bit differently. You need to destructure and rename the "default" key from the
182-
returned object.
183-
184-
```js
185-
(async () => {
186-
if (somethingIsTrue) {
187-
const { default: myDefault, foo, bar } = await import('/modules/my-module.js');
188-
}
189-
})();
190-
```
191-
192-
### Dynamic Imports
193-
194-
The standard import syntax is static and will always result in all code in the imported
195-
module being evaluated at load time. In situations where you wish to load a module
196-
conditionally or on demand, you can use a dynamic import instead. The following are some
197-
reasons why you might need to use dynamic import:
198-
199-
- When importing statically significantly slows the loading of your code and there is
200-
a low likelihood that you will need the code you are importing, or you will not need
201-
it until a later time.
202-
- When importing statically significantly increases your program's memory usage and
203-
there is a low likelihood that you will need the code you are importing.
204-
- When the module you are importing does not exist at load time
205-
- When the import specifier string needs to be constructed dynamically. (Static import
206-
only supports static specifiers.)
207-
- When the module being imported has side effects, and you do not want those side
208-
effects unless some condition is true. (It is recommended not to have any side effects
209-
in a module, but you sometimes cannot control this in your module dependencies.)
210-
211-
Use dynamic import only when necessary. The static form is preferable for loading
212-
initial dependencies, and can benefit more readily from static analysis tools and [tree shaking](/en-US/docs/Glossary/Tree_shaking).
213-
214-
To dynamically import a module, the `import` keyword may be called as a
215-
function. When used this way, it returns a promise.
216-
217-
```js
218-
import('/modules/my-module.js')
219-
.then((module) => {
220-
// Do something with the module.
221-
});
222-
```
223-
224-
This form also supports the `await` keyword.
181+
Importing a name called `default` has the same effect as a default import. It is necessary to alias the name because `default` is a reserved word.
225182

226183
```js
227-
let module = await import('/modules/my-module.js');
184+
import { default as myDefault } from '/modules/my-module.js';
228185
```
229186

230187
## Examples
@@ -260,30 +217,6 @@ getUsefulContents('http://www.example.com',
260217
data => { doSomethingUseful(data); });
261218
```
262219

263-
### Dynamic Import
264-
265-
This example shows how to load functionality on to a page based on a user action, in
266-
this case a button click, and then call a function within that module. This is not the
267-
only way to implement this functionality. The `import()` function also
268-
supports `await`.
269-
270-
```js
271-
const main = document.querySelector("main");
272-
for (const link of document.querySelectorAll("nav > a")) {
273-
link.addEventListener("click", e => {
274-
e.preventDefault();
275-
276-
import('/modules/my-module.js')
277-
.then(module => {
278-
module.loadPageInto(main);
279-
})
280-
.catch(err => {
281-
main.textContent = err.message;
282-
});
283-
});
284-
}
285-
```
286-
287220
## Specifications
288221

289222
{{Specifications}}
@@ -295,6 +228,7 @@ for (const link of document.querySelectorAll("nav > a")) {
295228
## See also
296229

297230
- {{JSxRef("Statements/export", "export")}}
231+
- [Dynamic imports](/en-US/docs/Web/JavaScript/Reference/Operators/import)
298232
- [`import.meta`](/en-US/docs/Web/JavaScript/Reference/Statements/import.meta)
299233
- Limin Zhu, Brian Terlson and Microsoft Edge Team:
300234
[Previewing ES6 Modules and more from ES2015, ES2016 and beyond](https://blogs.windows.com/msedgedev/2016/05/17/es6-modules-and-beyond/)

0 commit comments

Comments
 (0)