根本区别在于:静态导入使构建工具在编译前即可确定依赖图,支持Tree Shaking、作用域提升和确定性分包;动态导入将依赖决策推迟至运行时,仅能按需代码分割且无法预判使用路径。

根本区别在于:静态导入让构建工具在编译前就能“看清”整个依赖图,从而做 Tree Shaking、预分析和确定性分包;动态导入则把依赖决策推迟到运行时,构建工具只能按需拆包,但无法提前推断哪些代码会被用到。
静态导入:构建期可预测,优化空间大
import 语句必须写在模块顶层,路径必须是静态字符串(如 import { foo } from './utils.js')。Webpack、Vite 等工具在解析源码阶段就能完整收集所有 import 关系,形成确定的依赖图。这带来三个关键能力:
- Tree Shaking:能准确识别未被引用的导出(export),安全剔除死代码
- 作用域提升(Scope Hoisting):把多个模块合并为一个函数,减少闭包开销
- 确定性分包:配合魔法注释(如
/* webpackChunkName: "admin" */),可稳定控制 chunk 输出名和内容
动态导入:运行时才决定加载什么
import('./module.js') 是一个返回 Promise 的函数调用,路径可以是变量或模板字符串(如 import(`./locales/${lang}.js`)),只要构建工具能静态分析出可能的取值范围即可。它的打包行为特点是:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 自动触发代码分割:每个
import()调用都会生成独立 chunk,哪怕目标模块很小 - 无法参与主包的 Tree Shaking:因为导入时机不确定,构建工具不敢删掉该模块里“看似没用”的导出
- 模块缓存由引擎管理:同一路径第二次
import()会直接返回已加载的模块实例,不重复请求
require() 不是“动态导入”,而是“绕过静态分析”
CommonJS 的 require 在 Webpack 中虽能实现按需加载效果,但它本质上是运行时求值,构建工具无法静态判定依赖路径(尤其是带变量的 require(path))。结果是:
- 整个被 require 的文件及其所有依赖,都会被打包进主 bundle 或一个大 chunk 中
- Tree Shaking 基本失效:工具无法判断哪些 export 真正被用到
- 模块不是 ESM,失去命名空间对象、顶层 this 隔离、自动严格模式等原生优势
选哪个?看目标:要优化,就用静态 import;要延迟,就用 import()
静态 import 是默认选择——适合通用工具函数、核心业务逻辑、组件基础能力等确定性依赖;import() 是精准手术刀——适合路由级页面、条件弹窗、用户触发的功能模块(如编辑器插件、多语言包、权限隔离面板)。混用才是常态:主框架静态引入,边缘功能动态加载。

















