JavaScript语法特性篇-动态导入 import

时间:2025-03-28 19:03:06

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")是静态的,并且总是在加载时评估导入的模块。动态导入允许人们绕过导入声明的语法刚性,并条件性地或按需加载模块。以下是可能需要使用动态导入的一些原因:

  1. 当静态导入显著减慢代码加载速度或增加程序内存使用,并且你导入的代码被使用的可能性很低,或者直到稍后才需要它

  2. 当你要导入的模块在加载时不存在

  3. 当导入标识符字符串需要动态构造时。(静态导入仅支持静态标识符)

  4. 当被导入的模块有副作用,而你不想在条件不满足时产生这些副作用

  5. 当你处在一个非模块环境(例如,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
});