本文译自:Using ES modules natively in Node.js,原作者是 Dr. Axel Rauschmayer,圈内称之为”德国阮一峰”。
Node.js 从 8.5.0 开始原生支持 ES 模块,不过还需要通过命令行参数控制。这还得感谢 Bradley Farias 对这个新特性的贡献。
本文就给大家详细讲讲这个特性。
举个栗子
文件结构:
esm-demo/
lib.mjs
main.mjs
lib
.
mjs
:
exportfunctionadd(x, y) {
return x + y;
}
main
.
mjs
:
import {add} from'./lib.mjs';
console.log('Result: '+add(2, 3));
运行示例:
$ node --experimental-modules main.mjs
Result: 5
注意注意!
ES 的模块名称:
-
所有的模块名称都是 URL —— 这对 Node.js 来说引入了新的东西;
-
文件:最好使用相对的带
.
mjs
后缀的路径来引用 ES 模块,这样可以保证对浏览器的兼容。如果不包含后缀名,则模块搜索方式与 CJS 保持一致。如果同时找到了
.
mjs
和
.
js
的文件,则使用
.
mjs
文件;
-
例如:
../
util
/
tools
.
mjs
-
-
类库模块:直接使用模块名,省略扩展名是最佳方式。这能与现阶段类库模块发布的方式保持最好的兼容;
-
例如:
lodash
-
-
如何让
node_modules
里的模块在浏览器里工作还有待研究(不使用 bundler 工具)。一种可选方案是引入类 RequireJS 的配置方式,将裸路径转换到真实路径上。就目前而言,裸路径的模块在浏览器是中是用不了的;
ES 模块的特性:
-
无法动态引入模块;但是很快就会引入动态操作符
import
()
来实现;
-
像
__dirname
和
__filename
和这样的元变量将不再提供,有一个新的提案引入将会给 ES 模块提供类似的方式——Domenic Denicola 提出
import
.
meta
。
console.log(import.meta.url);
与 CJS 模块的互操作性
-
ES 模块可以
import
CJS 模块,但是引入的模块只包含一个
default
export——即
module
.
exports
的值。已经开始计划让 CJS 模块输出具名 export(通过在文件开始加编译指令的方式),不过可能实现需要花一些时间。如果你想帮忙,看这里。
-
import
fs1 from
'fs'
;
-
console
.
log
(
Object
.
keys
(
fs1
).
length
);
// 86
-
-
import
*
as fs2 from
'fs'
;
-
console
.
log
(
Object
.
keys
(
fs2
));
// ['default']
-
在 ES 模块中不能调用
require
()
,主要是因为:-
路径解析在两种规范中是很不一样的:ESM 不支持
NODE_PATH
和
require
.
extensions
;它的模块标识符永远都是 URL,这会造成小小的区别; -
为了与浏览器保持最大的兼容性,ES 模块总是异步加载的。这种异步加载的方式与 CJS 通过
require
()
同步加载的方式无法很好的在一起工作; -
同步模块的禁用同样也给在 ES 模块中使用顶级
await
关上了大门(一个目前正在考虑的特性);
-
-
CJS 无法
require
()
ES 模块。原因与上一条提到的类似,同步加载异步的模块各种问题啊。不过也许可以通过
import
()
来加载 ES 模块。在老版本的 Node.js 上使用 ES 模块
好吧,如果你想在老的 Node.js 的使用 ES 模块,可以看看 John-David Dalton 的
@std
/
esm
。小贴士:只要不使用 unlockables 特性,就可以保证与原生的 ES Module 完全兼容。
FAQ
什么时候可以不借助命令配置就可以使用 ES 模块?
目前的计划是,在 Node.js 10 LTS 版本中可以直接支持 ES 模块;
.
mjs
在浏览器中可以工作么?
可以的,只需要提供正确的 Media Type(
text
/
javascript
或者
application
/
javascript
)即可。
.
mjs
标准化和周边工具的升级正在进行中。文件扩展名
.
mjs
是 ES 模块所必须的么?
是的,对于 Node.js 来说是这样的。浏览器不在乎文件扩展名,只关心 Media Type。
为什么 Node.js 需要
.
mjs
?
Node.js 本可以检查一个文件是 CJS 模块还是 ES 模块。在选择添加扩展名这个方案之前也讨论过多个其他方案。可以阅读本博的另外一篇了解。每种方案都有其优势和缺点。在我看来,
.
mjs
是正确的选择。