1、基本使用
import()
语法,通常被称为动态导入,是一个类似函数的表达式,它允许异步和动态地将 ECMAScript 模块加载到一个可能不是模块的环境中。
与声明式的导入相对应,动态导入只在需要时进行计算,并且允许更大的语法灵活性。
简单来说,使用 import()
语法,你可以在运行时(而不是在编译时)决定要导入哪个模块,并且这种导入是异步的,不会阻塞代码的执行。
await import('/modules/')
2、与静态导入区别
静态导入是在 JavaScript 模块中声明时直接指定要导入的模块,是 ES6 中标准导入的方式。
//
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
//
// 静态导入 中函数
import { add, subtract } from './';
(add(1, 2)); // 输出 3
(subtract(3, 1)); // 输出 2
动态导入使用 import()
,其调用非常类似于函数调用语法,但 import 本身是一个关键字,不是一个函数。它返回一个 Promise,这个 Promise 解析为导入的模块对象。
假设需要再用户点击按钮后才加载并执行某个模块中的代码,可以采用点击时触发动态导入。
//
('#loadModuleButton').addEventListener('click', async () => {
try {
const module = await import('./');
// 导出了一个名为 dynamicFunction 的函数
(());
} catch (error) {
('Error loading module:', error);
}
});
//
export function dynamicFunction() {
return 'This is a dynamically loaded function!';
}
当用户点击 ID 为 loadModuleButton
的按钮时,JavaScript 会异步加载并执行 文件中的代码。
3、动态导入使用场景
导入声明语法(如 import something from "somewhere"
)是静态的,并且总是在加载时评估导入的模块。动态导入允许人们绕过导入声明的语法刚性,并条件性地或按需加载模块。以下是可能需要使用动态导入的一些原因:
-
当静态导入显著减慢代码加载速度或增加程序内存使用,并且你导入的代码被使用的可能性很低,或者直到稍后才需要它
-
当你要导入的模块在加载时不存在
-
当导入标识符字符串需要动态构造时。(静态导入仅支持静态标识符)
-
当被导入的模块有副作用,而你不想在条件不满足时产生这些副作用
-
当你处在一个非模块环境(例如,
eval
或一个脚本文件)时
仅在必要时使用动态导入。对于加载初始依赖项,静态形式更可取,并且可以更容易地从静态分析工具和“树摇”中受益。
如果你的文件不是作为模块运行的(如果它在 HTML 文件中被引用,则 <script>
标签必须具有 type="module"
),你将无法使用静态导入声明,但异步动态导入语法始终可用,允许你将模块导入到非模块环境中。
并非所有执行上下文都允许动态模块导入。例如,import ()可以在主线程、共享工作线程或专用工作线程中使用,但是如果在服务工作线程或工作线程中调用,则会抛出。
4、模块命名空间对象
模块命名空间对象是一个描述模块所有导出的对象。它是在模块评估时创建的静态对象。
有两种方法可以访问模块的模块命名空间对象:
-
通过命名空间导入(
import * as name from moduleName
) -
通过动态导入的 Promise fullfilled 值。
模块命名空间对象是一个封闭的、没有原型的对象。这意味着该对象的所有字符串键都对应于模块的导出,并且永远不会有多余的键。所有键都按照字典顺序可枚举(即 ()
的默认行为),默认导出可用作名为 default
的键。此外,模块命名空间对象有一个 @@toStringTag
属性,其值为 “Module”,可通过 ()
方法获取。
当你使用 ()
获取属性的描述符时,字符串属性是不可配置的和可写的。但是,它们实际上是只读的,因为你不能将一个属性重新赋值为一个新值。这种行为反映了静态导入创建“实时绑定”的事实——这些值可以由导出它们的模块重新赋值,但不能由导入它们的模块重新赋值。属性的可写性反映了值可能会改变的可能性,因为不可配置和不可写的属性必须是常量。例如,你可以重新分配一个变量的导出值,并在模块命名空间对象中观察到新值。
每个模块标识符都对应一个唯一的模块命名空间对象,因此以下两个导入方式得到的模块命名空间对象是一致的:
import * as mod from "/";
import("/").then((mod2) => {
(mod === mod2); // true
});