在Node中使用ES Modules


编者注:这篇文章来自John-David Dalton,微软Edge团队的项目经理以及Lodash库的创始人,分享一个将ECMAScript模块带到Node新社区项目的消息。

我很高兴宣布@std esm(standard / esm)的发布,这是一个可选且符合规范的ECMAScript(ES)模块加载器,能以接近内置模块的速度实现Node和ES Modules格式转换。这个快速、小型、零依赖的包可以在Node4+中启动ES Modules!

img1

在Node中运行@std/esm

两种模块格式的历史

随着ESM在浏览器的逐步运用,人们也希望Node未来也能支持ESM。但与浏览器不同的是,它是解析器解析的,没有预定的规范。所以在Node中支持ESM需要做的事情比较多。Node模块的规范CommonJS(CJS)是Node受欢迎的的重要原因,但CJS也使Node支持ESM变得复杂化。我们来看看两个模块语法的例子。

CJS:

const a = require("./a")
module.exports = { a, b: 2 }

esm:

import a from "./a"
export default { a, b: 2 }

注:如果想要更深入的比较,请参照Nicolás Bevacqua的文章 https://ponyfoo.com/articles/es6-modules-in-depth#the-es6-module-system

由于CJS和ESM不兼容,因此必须加以区分。经过讨论,Node已经确定使用”.mjs”(modular JavaScript)扩展来解析模块文件。Node具有通过文件扩展处理资源的历史。例如你需要.json文件,Node可以使用JSON.parse来处理和加载。

在2018年4月左右,Node v10里的ESM支持计划落地。这让开发者、尤其是包的作者陷入非常困难的境地。他们有如下选择:

以上的选择好像都没有那么完美,生态系统的建立需要跨越一些CJS和ESM的不同。

img2

集成桥接

@std/esm是填补模块间差异的包。由于Node现在已经支持了大多数ES2015的功能,@std/esm可以自由选择是否启用ESM。 这个模块加载器有以下特点:

与CJS的ESM解决方案不同的是,@std/esm在运行时根据需要执行最少的源码转换、处理和缓存。 运行时处理文件有很多优点。

标准的特点

默认值很重要。 @std/esm尽可能遵循Node的内置规范,这就意味着,默认情况下,ESM需要使用.mjs扩展。 @std/esm开箱即用,无需额外配置,并且支持:

其他功能

开发者希望一切都可配置。所以@std/esm允许在package.json中使用”@std/esm”字段来解锁其他功能。选项包括:

性能

在我开始之前,让我明确下面的部分: It’s still super early, mileage may vary, and results may be hand wavey!

PR #14369中已经通过Node 9编译测试完毕了,Node能支持内置ESM。 我测量了加载loadsh-es的643个模块并且转换为.mjs所花费的时间,提供一个加载的基准。请记住,@std/esm缓存能优化不修改的文件。这意味着理性情况下生产环境中只能有一个非缓存的负载。

初始化结果看起来还不错,很有希望,@std/esm使用缓存加载能接近内置模块的性能!我能肯定的是在你的帮助下,解析和运行时的性能将会不断提高。

入门

  1. 在你的APP或者包目录中运行npm i --save @std/esm
  2. 在导入ES modules之前调用require("@std/esm")
// index.js:
require("@std/esm")
module.exports = require("./main.mjs").default

// 对于子模块的包作者:

// 使用"foo"模块只需要
"@std/esm". require("foo")
// 子模块就能使用了
const bar = require("foo/bar").default

使用-r选项加载@std/esm能在Node CLI中启用ESM:

node -r @std/esm file.mjs

输入如下命令时,@std/esm在Node中启用ESM:

$ node
> require("@std/esm")
@std/esm enabled
> import p from "path"
undefined
> p.join("hello", "world")
'hello/world'

Meteor的力量

没有Ben Newman@std/esm加载器就不会存在,他是Reify编译器的作者,Reify就是来自@std/esm,他证明了在Meteor的生成环境下实现加载器,自从2016年5月以来,已经涌现了成千上万的Metor开发的应用。

优点(All green thumbs )

即使@std/esm才刚刚发布,它已经在很多相关项目上有了积极的影响:

下一步(What’s next)

和许多开发者一样,我也打算使用ES Modules。我打算在Lodash v5中使用@std esm,不仅可以转换到ESM,还支持gzip模块,这样能大大减少包的大小。

@std/esm加载器在GitHub上已经可以用了。我希望别人像我一样兴奋和充满激情。ES Modules来了!这只是开始。接下来是由你决定。我期待看到你使用它。

最后的一点想法(Final Thought)

虽然这不是微软的发行版,但我们为微软基础JavaScript框架、库以及程序代码的核心贡献者感到越来越自豪。贡献者如 Moment.jsMaggie PintReactiveXMatthew PodwysockiPouchDBNolan LawsonModernizrPatrick KettnerAureliaRob EisenbergWebpackSean Larkin,以及EmberTom Dale等等,他们正通过生态系统的标准来塑造JavaScript和整个网络的未来。我很高兴在Microsoft Edge博客上分享这个消息,与社区的朋友分享我们的热忱!