ESM(ECMAScript Modules)前端开发介绍与使用指南
一、什么是 ESM?
ECMAScript Modules(ESM) 是 JavaScript 语言官方标准化的模块系统,自 ECMAScript 2015(ES6) 起正式引入,并在后续版本中不断完善。作为现代 Web 开发的基石,ESM 解决了长期以来 JavaScript 缺乏原生模块化支持的问题。
模块化历史演进
阶段
方案
特点
早期
全局变量模式
功能挂载到全局对象,易冲突
中期
CommonJS
Node.js 使用,同步加载
中期
AMD/UMD
浏览器异步加载,配置复杂
现代
ESM
原生标准,静态分析,树摇优化
二、ESM 核心语法
1. 导出(Export)
复制代码
// 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
// 默认导出(每个模块只能有一个)
export default function main() {
console.log('默认导出');
}
// 批量导出
export { PI, add, main };
// 重命名导出
export { PI as MathPI };
2. 导入(Import)
复制代码
// 导入默认导出
import main from './module.js';
// 导入命名导出
import { PI, add } from './module.js';
// 导入全部
import * as utils from './module.js';
// 重命名导入
import { PI as CirclePI } from './module.js';
// 动态导入(按需加载)
const module = await import('./module.js');
3. ES2025 新特性:JSON 模块支持
复制代码
// 无需 fetch + JSON.parse,直接导入
import configData from './config-data.json' with { type: 'json' };
// 静态导入
import packageInfo from './package.json' assert { type: 'json' };
三、前端项目中使用 ESM
1. HTML 中直接使用
复制代码
<!DOCTYPE html>
<html>
<head>
<title>ESM 示例</title>
</head>
<body>
<!-- 使用 type="module" -->
<script type="module" src="./main.js"></script>
<!-- 内联模块 -->
<script type="module">
import { add } from './utils.js';
console.log(add(1, 2));
</script>
</body>
</html>
2. 构建工具配置
Vite(推荐,原生 ESM 支持)
复制代码
// vite.config.js
export default {
// Vite 默认使用 ESM,无需额外配置
optimizeDeps: {
esbuildOptions: {
target: 'es2020'
}
}
}
Webpack
复制代码
// webpack.config.js
module.exports = {
experiments: {
outputModule: true // 启用 ESM 输出
},
output: {
module: true,
chunkFormat: 'module'
}
}
package.json 配置
复制代码
{
"type": "module", // 启用 ESM
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}
四、ESM vs CommonJS 对比
特性
ESM
CommonJS
语法
import/export
require/module.exports
加载方式
静态分析,编译时加载
动态加载,运行时加载
执行时机
模块加载时执行
调用时执行
绑定方式
活绑定(引用)
值拷贝
树摇优化
✅ 支持
❌ 不支持
浏览器支持
✅ 原生支持
❌ 需转换
异步加载
✅ import()
❌ 需额外处理
五、最佳实践
1. 模块组织
复制代码
src/
├── index.js # 入口文件
├── components/ # 组件模块
├── utils/ # 工具函数
├── services/ # API 服务
└── styles/ # 样式文件
2. 代码分割与懒加载
复制代码
// 路由级代码分割
const Home = () => import('./pages/Home.vue');
const About = () => import('./pages/About.vue');
// 条件加载
if (condition) {
const module = await import('./heavy-module.js');
module.init();
}
3. 避免循环依赖
复制代码
// ❌ 不良实践:循环引用
// a.js
import { b } from './b.js';
// b.js
import { a } from './a.js';
// ✅ 解决方案:提取公共模块
// common.js
export const shared = {};
// a.js & b.js 都从 common.js 导入
4. 纯 ESM 包发布(2025 趋势)
根据 Vue/Vite 核心团队成员 Anthony Fu 的建议,2025 年推荐发布纯 ESM 包 :
复制代码
{
"type": "module",
"exports": {
".": "./dist/index.js"
},
"engines": {
"node": ">=18.0.0"
}
}
六、兼容性处理
1. 浏览器支持
浏览器
最低支持版本
Chrome
61+
Firefox
60+
Safari
10.1+
Edge
16+
2. 降级方案
复制代码
<!-- 模块脚本 -->
<script type="module" src="./app.js"></script>
<!-- 降级脚本(不支持 module 的浏览器) -->
<script nomodule src="./app-legacy.js"></script>
3. Babel 转换配置
复制代码
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: {
browsers: ['>0.25%', 'not dead']
},
modules: false // 保留 ESM 语法
}]
]
}
七、ES2025 ESM 新特性
特性
说明
状态
JSON 模块导入
原生支持 .json 文件导入
✅ 已发布
Import Attributes
with { type: 'json' } 语法
✅ 已发布
延迟模块评估
优化模块加载性能
✅ 已发布
动态 import() 增强
更好的错误处理
✅ 已发布
复制代码
// ES2025 JSON 模块示例
import config from './config.json' with { type: 'json' };
console.log(config.apiKey);
// 错误处理
try {
const module = await import('./optional-module.js');
} catch (error) {
console.error('模块加载失败:', error);
}
八、常见问题与解决方案
1. CORS 错误
复制代码
// 确保服务器设置正确的 CORS 头
// Access-Control-Allow-Origin: *
2. 路径问题
复制代码
// ✅ 使用完整路径(带扩展名)
import { util } from './utils/util.js';
// ❌ 避免省略扩展名
import { util } from './utils/util';
3. Node.js 中使用 ESM
复制代码
// package.json
{
"type": "module"
}
// 或使用 .mjs 扩展名
// app.mjs
import fs from 'fs';
九、总结
优势
说明
🎯 标准化
官方规范,跨平台统一
🚀 性能优化
静态分析支持树摇
📦 代码组织
清晰的依赖关系
🔒 安全性
严格模式,作用域隔离
🌐 浏览器原生
无需额外工具即可使用
2026 年建议 :
✅ 新项目直接使用 ESM
✅ 构建工具优先选择 Vite
✅ 发布 npm 包推荐纯 ESM
⚠️ 老项目逐步迁移,注意兼容性
ESM 已成为现代前端开发的标准配置 ,掌握 ESM 是前端开发者的必备技能!