我们在前面章节中介绍的导出和导入语句称为“静态”导入。语法非常简单且严格。

首先,我们不能动态生成 import 的任何参数。

模块路径必须是原始类型字符串,不能是函数调用,下面这样的 import 行不通:

  1. import ... from getModuleName(); // Error, only from "string" is allowed

其次,我们无法根据条件或者在运行时导入:

  1. if(...) {
  2. import ...; // Error, not allowed!
  3. }
  4. {
  5. import ...; // Error, we can't put import in any block
  6. }

这是因为 import/export 旨在提供代码结构的主干。这是非常好的事儿,因为这样便于分析代码结构,可以收集模块,可以使用特殊工具将收集的模块打包到一个文件中,可以删除未使用的导出(“tree-shaken”)。这些只有在 import/export 结构简单且固定的情况下才能够实现。

但是,我们如何才能动态地按需导入模块呢?

import() 表达式

import(module) 表达式加载模块并返回一个 promise,该 promise resolve 为一个包含其所有导出的模块对象。我们可以在代码中的任意位置调用这个表达式。

我们可以在代码中的任意位置动态地使用它。例如:

  1. let modulePath = prompt("Which module to load?");
  2. import(modulePath)
  3. .then(obj => <module object>)
  4. .catch(err => <loading error, e.g. if no such module>)

或者,如果在异步函数中,我们可以使用 let module = await import(modulePath)

例如,如果我们有以下模块 say.js

  1. // 📁 say.js
  2. export function hi() {
  3. alert(`Hello`);
  4. }
  5. export function bye() {
  6. alert(`Bye`);
  7. }

……那么,可以想像下面这样进行动态导入:

  1. let {hi, bye} = await import('./say.js');
  2. hi();
  3. bye();

或者,如果 say.js 有默认的导出:

  1. // 📁 say.js
  2. export default function() {
  3. alert("Module loaded (export default)!");
  4. }

……那么,为了访问它,我们可以使用模块对象的 default 属性:

  1. let obj = await import('./say.js');
  2. let say = obj.default;
  3. // or, in one line: let {default: say} = await import('./say.js');
  4. say();

这是一个完整的示例:

结果

say.js

index.html

  1. export function hi() {
  2. alert(`Hello`);
  3. }
  4. export function bye() {
  5. alert(`Bye`);
  6. }
  7. export default function() {
  8. alert("Module loaded (export default)!");
  9. }
  1. <!doctype html>
  2. <script>
  3. async function load() {
  4. let say = await import('./say.js');
  5. say.hi(); // Hello!
  6. say.bye(); // Bye!
  7. say.default(); // Module loaded (export default)!
  8. }
  9. </script>
  10. <button onclick="load()">Click me</button>

请注意:

动态导入在常规脚本中工作时,它们不需要 script type="module".

请注意:

尽管 import() 看起来像一个函数调用,但它只是一种特殊语法,只是恰好使用了括号(类似于 super())。

因此,我们不能将 import 复制到一个变量中,或者对其使用 call/apply。因为它不是一个函数。