← 返回题目列表

动态 import 和 top-level await 分别适合什么场景?

中等 第 26 / 28 题 更新于 2026/07/29
ES6Module动态导入

简化版

动态 import() 可以在运行时按需加载模块,返回一个 Promise,常用于路由级代码分割、条件加载、重型依赖延迟加载。top-level await 允许在 ES Module 顶层直接等待异步结果。

动态导入强调“什么时候加载”,top-level await 强调“模块初始化可以等待异步依赖”。它们都可能影响加载链路,需要谨慎用于首屏关键路径。

详细版

动态导入:

button.addEventListener("click", async () => {
  const { openEditor } = await import("./editor.js");
  openEditor();
});

top-level await:

const config = await fetch("/config.json").then(res => res.json());

export { config };

如果某个模块使用 top-level await,依赖它的模块会等待它初始化完成。用于配置、WASM 初始化等场景时很方便,但放在核心依赖上可能阻塞整条模块图。

完整版教学

一、先区分两种能力

动态 import() 是表达式,可以出现在函数、条件分支、事件回调里。

top-level await 是模块顶层能力,让模块初始化阶段可以等待异步操作。

能力关注点返回或影响
import()按需加载模块Promise
top-level await模块初始化等待阻塞依赖方执行

一个控制加载时机,一个控制模块初始化节奏,别把它们混成同一个概念。

二、动态 import 用于按需加载

async function openChart() {
  const { renderChart } = await import("./chart.js");
  renderChart();
}

这会给打包工具代码分割的机会,把不需要首屏加载的代码拆到单独 chunk。

比如一个管理后台首屏 300KB,图表编辑器 500KB,动态导入可以让 500KB 延后到用户真正点击时再加载。

三、动态 import 适合条件加载

if (locale === "zh-CN") {
  await import("./locale/zh-CN.js");
} else {
  await import("./locale/en-US.js");
}

它也适合只在浏览器环境加载某些库。

if (typeof window !== "undefined") {
  await import("./browser-only.js");
}

四、top-level await 改变模块执行顺序

// config.js
export const config = await loadConfig();

依赖 config.js 的模块会等待它完成。

main.js -> config.js -> await loadConfig()
main.js waits

这让异步初始化更自然,但也可能让启动链路变长。

五、循环依赖和阻塞风险

模块图里若存在复杂循环依赖,再叠加 top-level await,理解成本会明显上升。

a.js awaits b.js
b.js imports a.js

这种结构容易让初始化顺序难以推理。工程上应避免在基础模块、公共工具模块里随意放 top-level await。

六、打包和运行环境注意

动态导入通常能被现代打包工具识别为分包边界,但变量路径太动态会影响静态分析。

import(`./pages/${name}.js`);

这类写法可能生成上下文模块,把一批候选文件都打进去。top-level await 则需要目标运行环境和构建链支持 ES Module 语义。

七、常见误区与追问

  • 误区:动态 import 一定提升性能。 如果拆分过细或命中首屏路径,可能增加请求和等待。
  • 误区:import() 和静态 import 只有写法不同。 前者是运行时加载表达式,后者是静态依赖声明。
  • 误区:top-level await 只影响当前模块。 依赖它的模块也会等待其初始化完成。
  • 误区:所有模块都适合顶层 await 配置。 基础模块使用它会放大阻塞范围。
  • 追问:动态 import 返回什么? 返回解析为模块命名空间对象的 Promise。
  • 追问:变量路径为什么要谨慎? 它可能降低打包工具静态分析能力,导致多打包或分包不清晰。

八、加强记忆

记忆方式:

import()          -> load later
top-level await   -> initialize later
static import     -> declare dependency early

面试回答时先分清加载时机和初始化等待,再讲代码分割、条件加载、配置初始化,最后补充首屏阻塞和模块图复杂度风险。