我们在前几章中介绍的导出和导入语句被称为“静态”语句。语法非常简单且严格。
首先,我们无法动态生成 import
的任何参数。
模块路径必须是一个原始字符串,不能是函数调用。以下代码将无法运行
import ... from getModuleName(); // Error, only from "string" is allowed
其次,我们无法有条件地或在运行时进行导入
if(...) {
import ...; // Error, not allowed!
}
{
import ...; // Error, we can't put import in any block
}
这是因为 import
/export
旨在为代码结构提供一个主干。这是一件好事,因为代码结构可以被分析,模块可以被收集并打包到一个文件中,未使用的导出可以被移除(“tree-shaken”)。这只有在导入/导出的结构简单且固定时才有可能。
但是我们如何按需动态导入模块?
import() 表达式
import(module)
表达式加载模块并返回一个 promise,该 promise 解析为包含其所有导出的模块对象。它可以在代码中的任何位置调用。
我们可以在代码的任何位置动态使用它,例如
let modulePath = prompt("Which module to load?");
import(modulePath)
.then(obj => <module object>)
.catch(err => <loading error, e.g. if no such module>)
或者,如果在 async 函数内部,我们可以使用 let module = await import(modulePath)
。
例如,如果我们有以下模块 say.js
// 📁 say.js
export function hi() {
alert(`Hello`);
}
export function bye() {
alert(`Bye`);
}
…那么动态导入可以这样
let {hi, bye} = await import('./say.js');
hi();
bye();
或者,如果 say.js
有默认导出
// 📁 say.js
export default function() {
alert("Module loaded (export default)!");
}
…那么,为了访问它,我们可以使用模块对象的 default
属性
let obj = await import('./say.js');
let say = obj.default;
// or, in one line: let {default: say} = await import('./say.js');
say();
以下是完整示例
结果
say.js
index.html
export function hi() {
alert(`Hello`);
}
export function bye() {
alert(`Bye`);
}
export default function() {
alert("Module loaded (export default)!");
}
<!doctype html>
<script>
async function load() {
let say = await import('./say.js');
say.hi(); // Hello!
say.bye(); // Bye!
say.default(); // Module loaded (export default)!
}
</script>
<button onclick="load()">Click me</button>
请注意
动态导入在常规脚本中起作用,它们不需要 script type="module"
。
请注意
尽管 import()
看起来像函数调用,但它是一种特殊的语法,碰巧使用括号(类似于 super()
)。
因此,我们无法将 import
复制到变量中或使用 call/apply
。它不是一个函数。
评论
<code>
标签,对于多行 - 将其包装在<pre>
标签中,对于 10 行以上 - 使用沙箱(plnkr,jsbin,codepen…)