动态导入是esm静态依赖图的运行时补充,解决“是否/何时加载”,但不改变导出契约;路径须为字面量,需构建工具支持拆包,应封装为带加载态与错误处理的业务函数,仅用于显著增重或非首屏必需模块。

精通 JS 动态导入,关键不是会写 import(),而是理解它在模块系统中的定位——它是对静态 ESM 依赖图的**运行时补充**,不是替代。它解决的是“要不要加载”和“什么时候加载”,但绝不松动“能导出什么、谁可以访问”的契约边界。
紧扣静态性前提,不越界使用
动态导入不能掩盖 ESM 的底层铁律:模块结构仍需可静态分析。
- 路径必须是字符串字面量或可推断常量,禁止拼接变量:
import('./' + name + '.js')或import(`./${lang}.js`)在构建阶段直接报错 -
import()可以出现在任意位置(函数内、条件块、循环中),但高频调用需谨慎:循环里反复import('./item.js')会触发大量并发请求,应聚合或节流 - 它返回 Promise,但不改变模块本身的导出形态:如果目标模块只导出默认值,你就得用
module.default;若只有具名导出,解构时不能漏掉{ render, init }
与构建工具深度协同,确保真正拆包
写了 import() 不等于自动切 chunk——构建器必须识别并响应这个信号。
- Vite 默认支持:只要
<script type="module"></script>存在,import('./admin.js')就会生成独立文件如admin.abc123.js,无需额外配置 - Webpack 需确认启用
experiments.topLevelAwait: true(v5.87+),否则顶层await import()会编译失败 - 检查产物目录(如
dist/assets/)是否有对应 chunk 文件,是验证是否生效的最直接方式;没有,说明被内联或忽略
封装成可复用、带反馈的业务单元
把动态导入从语法操作升级为用户可感知的功能模块。
- 用
async/await封装,避免嵌套.then():把加载逻辑收进函数,比如loadChartModule(),调用方只关心结果 - 必加错误处理:捕获
import()失败,给出明确 UI 提示(如“图表功能暂不可用,请稍后重试”),而非仅console.error - 搭配加载状态:按钮点击后显示 loading 指示器,模块就绪再渲染内容,避免白屏等待感
精准识别适用场景,拒绝滥用
动态导入的价值在于“按需”,而不是“看起来高级”。
- 推荐场景:路由级页面(报表页、后台管理)、权限隔离模块(管理员专属设置)、重型依赖(PDF 渲染、WASM 工具)
- 不推荐场景:几个小工具函数、基础工具库(如
lodash-es)、首屏必需逻辑——静态引入更稳定、更利于 Tree Shaking - 一个判断标准:该模块加载后是否显著增大首屏体积?是否在多数用户会话中根本不会执行?如果是,才值得动态化











