ESM 模块化语法解析
🎯 引言
学完这篇文章,你将理解 ES6 的 ESM(ECMAScript Modules)模块化标准,掌握如何使用 import 和 export 来拆分和组织代码。这对于提升代码的可维护性和复用性非常重要。还能更好地适配现代前端构建工具和浏览器环境,避免命名冲突,让你写出更有条理的 JavaScript 应用。
📦 什么是 ESM 模块化?
模块化其实就是把复杂的代码拆解成一个个小块,每块专注做一件事。在之前的 JavaScript 世界里,模块化方案比较多,比如 CommonJS(Node.js 常用)和 AMD,但它们都不是标准。ES6 以后,JavaScript 官方推出了自己的模块化机制,叫 ESM(ECMAScript Module),简称模块。
ESM 是 JavaScript 的原生模块系统,浏览器和 Node.js 都支持。它允许你使用 export 导出变量、函数或类,再用 import 把它们引入到别的文件中,完美实现代码拆分和复用。
🚀 ESM 的基本用法
导出(export)
你可以导出变量、函数、对象,甚至类:
// math.js
export const PI = 3.14159;
export function add(x, y) {
return x + y;
}
export class Calculator {
subtract(a, b) {
return a - b;
}
}
你还可以一次性导出:
const name = 'ES6 ESM';
function greet() {
console.log('Hello ESM!');
}
export { name, greet };
导入(import)
导入时,可以选择导入所有导出的成员,或者部分导入:
import { PI, add } from './math.js';
console.log(PI); // 3.14159
console.log(add(2, 3)); // 5
你也可以给导入的内容起别名:
import { add as sum } from './math.js';
console.log(sum(5, 6)); // 11
导入整个模块到一个对象:
import * as math from './math.js';
console.log(math.PI);
console.log(math.add(1, 2));
🧩 默认导出和命名导出
ESM 支持两种导出方式:
- 命名导出:上面例子用的就是,导出时写名字,导入时也用对应名字。
- 默认导出:一个模块可以有且只有一个默认导出,适合导出主功能。
举个默认导出例子:
// logger.js
export default function log(message) {
console.log('Log:', message);
}
导入默认导出时,不用花括号:
import log from './logger.js';
log('Hello ESM!');
默认导出也可以是变量、对象、数组或类:
export default {
version: '1.0',
info() {
console.log('ESM 模块信息');
},
};
🔄 ESM 特点和注意事项
- ESM 文件默认采用严格模式(
strict mode),不用自己写'use strict'。 - 模块内的变量默认是模块作用域,外部访问不到。
- 导入是静态分析的,编译时就确定依赖关系,不支持动态加载(ES2020 有动态
import(),稍后会说)。 - 不能在模块顶层使用
var,推荐用let和const。 - 模块加载是异步的,但浏览器会保证模块之间的依赖顺序。
- ESM 只支持相对路径或绝对路径导入,不能省略扩展名(在浏览器环境中)。
🧪 实际应用场景举例
假设你有一个网页项目,写了很多函数处理日期、字符串和数组。把它们放一个文件里,代码会越来越乱。利用 ESM,把功能拆成不同模块:
// dateUtils.js
export function formatDate(date) {
return date.toISOString().slice(0, 10);
}
// stringUtils.js
export function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// arrayUtils.js
export function unique(arr) {
return [...new Set(arr)];
}
主文件里这样引用:
import { formatDate } from './dateUtils.js';
import { capitalize } from './stringUtils.js';
import { unique } from './arrayUtils.js';
console.log(formatDate(new Date()));
console.log(capitalize('hello'));
console.log(unique([1, 2, 2, 3]));
这样结构清晰,便于维护,也方便多人协作。
提示
使用 ESM 时,浏览器环境下要通过 <script type="module"> 标签来引入模块:
<script type="module" src="./main.js"></script>
这样浏览器才会识别这是模块,会支持 import 和 export。
注意
如果想在 Node.js 中使用 ESM 模块,你需要在 package.json 中设置 "type": "module",或者用 .mjs 后缀,否则默认是 CommonJS。不然会出现导入错误。
🔎 动态导入 import()
ES2020 引入动态导入语法,可以动态加载模块,返回一个 Promise:
async function loadModule() {
const math = await import('./math.js');
console.log(math.add(1, 2)); // 3
}
loadModule();
这是解决按需加载、代码分割的利器。
🧾 小节总结
- ES6 ESM 是 JavaScript 的官方模块化方案,使用
import和export来拆分代码。 - 支持命名导出和默认导出,导入时可以选择性引入模块成员。
- 使用模块化可以让代码结构更清晰,方便维护和复用。
❓ 知识问答(Q&A)
Q:ESM 模块与 CommonJS 有什么区别?
A:ESM 是 JavaScript 标准模块系统,支持静态分析,导入导出语法是静态的;CommonJS 是 Node.js 早期模块规范,导入是动态的,用 require,不支持浏览器原生。
Q:模块文件扩展名可以省略吗?
A:浏览器环境中,导入模块一般不能省略 .js 后缀,Node.js 根据配置可省略。
Q:什么是默认导出?
A:模块只能有一个默认导出,导入时不用花括号,可以给默认导出起任意名字。
🧪 小练习
- 创建一个名为
utils.js的模块,导出两个函数:double(传入数字,返回两倍数字)和默认导出函数square(传入数字返回平方)。然后在另一个文件导入并使用它们。
// utils.js
// 在这里写代码
// main.js
// 在这里写代码,导入并使用 double 和 square 函数
- 使用动态导入语法,按需加载上面写的模块,并调用其中的函数。
// main.js
// 在这里写代码,动态导入 utils.js 并调用函数
🎉 恭喜你已经掌握 ES6、ESM 模块化、静态导入和动态导入技能啦!前端代码组织变得更简单,更强大!
