JavaScript 的静态模块声明(ES6 Modules)要求在代码执行前确定模块依赖关系。
核心特点包括:
- 导入/导出必须在顶层作用域,不能动态生成;
- 支持编译时分析,实现Tree Shaking优化;
- 提供循环依赖安全处理;
与CommonJS的动态加载不同,ES6模块路径必须静态确定。
动态导入(
import())是 ES2020 引入的,它打破了完全静态的限制。
动态导入(import())作为补充允许运行时加载。
优势包括提前错误检测、更好的代码优化和打包体积缩减。
这种设计平衡了灵活性与性能,更适合大型应用开发。
在 JavaScript 中,静态模块声明 指的是在模块系统(ES6 Modules)中,模块的依赖关系在代码执行前就能被确定和分析的特性。
核心概念
1. 静态分析
模块的导入(import)和导出(export)语句必须在顶层作用域,不能动态生成:
// ✅ 正确的静态声明
import { foo } from './module.js';
export const bar = 'value';
// ❌ 错误:动态导入(这是动态的,不是静态声明)
if (condition) {
import { foo } from './module.js'; // 语法错误
}
// ❌ 错误:动态模块路径
import { foo } from somePath; // 语法错误
2. 与 CommonJS 的对比
// CommonJS (动态)
const modulePath = './' + name + '.js';
const module = require(modulePath); // 运行时解析
// ES6 Modules (静态)
import { something } from './fixed-path.js'; // 解析时确定
主要特点
编译时分析
-
打包工具(Webpack、Rollup)能在打包时分析依赖树
-
实现Tree Shaking(消除未使用的代码)
-
优化打包体积
确定性
// 这些在解析阶段就能确定
import { a } from './a.js';
import { b } from './b.js';
export { c } from './c.js';
支持动态导入作为补充
// 动态导入(返回 Promise)
async function loadModule() {
const module = await import('./module.js');
// 动态部分
}
// 但顶级导入必须是静态的
import { staticExport } from './static.js'; // 必须在顶层
优势
提前错误检测
import { nonExistent } from './module.js';
// 如果模块没有导出 nonExistent,构建时会报错
更好的优化
// 打包工具可以移除未使用的导出
import { usedFunc } from './utils.js';
// 如果 utils.js 还有其他导出但未使用,会被 tree-shaking
循环依赖更安全
// a.js
import { b } from './b.js';
export const a = 'A';
// b.js
import { a } from './a.js';
export const b = 'B';
// 静态分析可以处理这种循环依赖
实践示例
静态模块声明:
// math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;
// app.js
import { add } from './math.js'; // 静态声明
// 打包时知道只需要 add 函数
// 动态导入(非静态)
const operation = 'add';
const mathModule = await import('./math.js'); // 运行时加载
命名空间导入也是静态的:
import * as utils from './utils.js';
// 路径 './utils.js' 必须是字面量,不能动态生成
例外情况
动态导入 (import())是 ES2020 引入的,它打破了完全静态的限制:
// 这是允许的,但它是函数调用,不是声明
const modules = ['a', 'b', 'c'].map(name => import(`./${name}.js`));
总结
静态模块声明是 ES6 模块系统的核心设计,它通过:
-
在编译时确定依赖关系
-
启用高级优化(Tree Shaking)
-
提供更好的工具支持
-
保证模块加载的可预测性
这种设计权衡了灵活性和性能,使 JavaScript 模块系统更适合大型应用开发。