Node.js 模块系统 -- ESM


ECMAScript 模块(也称为 ES 模块或 ESM)作为 ECMAScript 2015 规范的一部分被引入,目的是为 JavaScript 提供一个适合不同执行环境的官方模块系统。

ESM 和 CommonJS 的最大不同是前者是静态的。每个模块的导入是在顶部,不能在任何控制流中;同时,导入模块的名称不能在运行时使用表达式生成。

在 Node.js 中使用 ESM

Node.js 默认 .js 文件是使用 CommonJS 语法编写的。因此,如果直接在 .js 文件中使用 ESM ,解释器会报错。

下面的方法可以告诉 Node.js 解释器,给定的模块文件使用的是 ESM

  • 直接使用 .mjs 后缀;
  • 或将 package.json 文件的 type 字段修改为 module;此时,使用 .js 文件可使用 ESM

导出和导入

使用 export 关键字进行导出。

// logger.js
export function log(message) {
	console.log(message)
}

export const DEFAULT_LEVEL = 'info'

export const LEVEL = {
	error: 0,
	debug: 1,
	warn: 2,
	data: 3,
	info: 4,
	verbose: 5
}

export class Logger {
	constructor(name) {
		this.name = name
	}
	log(message) {
		console.log(`[${this.name}] ${message}`)
	}
}

从模块中导入需要使用 import 关键字。可以方便的导入一个或多个实体,甚至进行重命名。例如:

import * as loggerModule from './logger.js'

* 会导入模块的所有成员,并使用 as 命名为 loggerModule。此时可以通过 loggerModule 访问所有的导出成员。 如果只想导入一个或某几个成员,使用 {}

import { log } from './logger.js'
import { log, Logger } from './logger.js'

使用这种方式导入的实体,可能存在命名冲突的问题。此时可使用 as 关键字进行重命名。

默认导出 - export default

默认导出使用 export default 关键字。

// logger.js
export default class Logger {
	constructor(name) {
		this.name = name
	}
	log(message) {
		console.log(`[${this.name}] ${message}`)
	}
}

这种情况下 Logger 名称被忽略,导出的实体被注册在 default 名称下。可使用如下方式导入:

// main.js
import MyLogger from './logger.js'
const logger = new MyLogger('info')
logger.log('Hello World')