ES 模块:JavaScript 模块化的标准方案
什么是 ES 模块?
ES 模块(ES Modules,简称 ESM)是 ECMAScript 2015(ES6)引入的官方模块化规范。
ES 模块 vs CommonJS
| 特性 | CommonJS | ES Modules |
|---|---|---|
| 加载方式 | 同步 | 异步 |
| 执行时机 | 运行时 | 编译时 |
| 导出 | module.exports | export |
| 导入 | require() | import |
| 顶层 this | module.exports | undefined |
基本用法
导出
javascript
// utils.js
// 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
// 默认导出
export default function greet(name) {
return `Hello, ${name}!`;
}
导入
javascript
// main.js
// 导入命名导出
import { PI, add } from './utils.js';
// 导入默认导出
import greet from './utils.js';
// 重命名导入
import { add as sum } from './utils.js';
// 导入所有
import * as utils from './utils.js';
动态导入
javascript
// 动态加载模块
async function loadModule() {
const module = await import('./utils.js');
console.log(module.add(2, 3));
}
// 条件加载
if (condition) {
import('./feature.js').then(module => {
module.init();
});
}
模块解析
文件扩展名
javascript
// 必须包含扩展名
import { func } from './utils.js';
// 不能省略
import { func } from './utils'; // ❌
绝对路径
javascript
// 从 node_modules 导入
import React from 'react';
// 绝对路径导入
import { utils } from '/path/to/utils.js';
模块作用域
javascript
// 模块顶层变量不会污染全局作用域
const privateVar = 'secret';
// 只有导出的内容才能被外部访问
export const publicVar = 'public';
循环依赖
javascript
// a.js
import { b } from './b.js';
export const a = 'a';
// b.js
import { a } from './a.js';
export const b = 'b';
在浏览器中使用
html
<script type="module" src="main.js"></script>
javascript
// main.js
import { greet } from './utils.js';
console.log(greet('World'));
在 Node.js 中使用
json
{
"type": "module"
}
javascript
// package.json 设置后可以使用 ESM
import fs from 'fs';
import path from 'path';
模块打包
Webpack 配置
javascript
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
}
};
Rollup 配置
javascript
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'esm'
}
};
最佳实践
1. 保持导出简洁
javascript
// ❌ 不好:导出过多
export const a = 1;
export const b = 2;
export const c = 3;
// ✅ 好:按需导出
export { a, b } from './constants.js';
export { default as c } from './c.js';
2. 使用命名导出
javascript
// ✅ 推荐:命名导出便于 tree-shaking
export function util1() {}
export function util2() {}
3. 避免循环依赖
javascript
// ❌ 不好:循环依赖
// a.js
import { b } from './b.js';
// b.js
import { a } from './a.js';
总结
ES 模块为 JavaScript 提供了标准化的模块化方案:
- 静态分析:支持 tree-shaking
- 异步加载:更好的性能优化
- 标准规范:跨平台兼容
- 清晰语义:明确的导入导出语法
掌握 ES 模块是现代前端开发的必备技能。