原文:Announcing a new --experimental-modules
译文地址
在2017年,Node.js 8.9.0发布了对ECMAScript模块的实验性支持。这种ECMAScript模块的支持是需要在后面加上--experimental-modules
标识来运行。
现在,主流浏览器都能通过标签支持ECMAScript 模块 (ES modules) 。各种项目npm包都使用了ES模块编写,并且可以通过
在浏览器中直接使用。支持导入映射(import maps)即将登陆Chrome。import map将让浏览器支持node.js风格的包名导入。
这些采用ES模块方面的进展大大加快了Node.js对ES模块的支持速度。既然已经有其他运行时和环境在使用ES模块,那么Node.js支持这个JavaScript标准就更重要了。
Nodejs最初将ES模块作为一个保留实验特性的原因是为了提供时间让社区去讨论和反馈这种设计。Modules Team建立出来就是为了对这些反馈提供支持,最终导致了一个ES模块的新实现。我们很高兴地宣布它将作为Node.js 12版本的一部分发布。它将取代旧的实验模块实现,并且将使用相同的标志(--experimental-modules
)。我们希望这个新实现能够解决社区的许多问题,并且能够在2019年10月Node.js 12达到LTS状态之前作为Node.js的一部分正式发布(不需要再带--experimental-modules
)
--experimental-modules
包含了什么
与之前的版本一样,这个新的实验性模块为Node.js增加了以下支持:
-
ES2015 import语句可以引用那些使用ES模块语法编写的Javascript文件。文件可以被引用为相对url (
'./file.mjs '
)、绝对file:// url ('file:///opt/app/file.mjs '
)、包名(' es-module-package '
)或包名路径('es-module-package/lib/file.mjs '
)的形式。 -
import语句引用的ES模块文件可以指定默认导出(
import _ from ‘es-module-package’
)、命名导出(import { shuffle } from ‘es-module-package’
) 和命名空间导出(import * as fs from ‘fs’
)。所有的Node.js内置模块(如fs和path)都支持这三种类型的导出。 -
import语句引用CommonJS文件(当前所有使用require、module.exports编写的Node.js模块)只能使用CommonJS默认导出(
import _ from ‘commonjs-package’
)。这项工作在进展中并且未来可能会发生改变 -
ES模块文件中的export语句可以指定引用的导入语句为默认导出或命名导出。
-
动态
import()
表达式可以用来从CommonJS或者ES模块导入ES模块文件。需要注意的是,这个语句返回的是一个promise -
import.meta.url
的值为当前ES模块文件的绝对url。 -
可以编写加载器(Loaders)来修改Node.Js对于ES模块的行为。这项工作仍然在进行中
-
Node.js可以将ES模块文件作为程序的初始入口运行
-
文件作为ES模块被加载是在严格模式下的,如果是CommonJS的话需要在每个文件的最头部加上
'use strict'
-
.mjs
结尾的文件在import语句和通过node命令行运行时都将被显式当作ES模块
并且,新版本的 --experimental-modules
添加了以下特性:
.js文件中的import和export语法
我们听到了一些非常强烈的反馈Nodejs需要提供一种方式在.js文件中使用import和export语法
新的--experimental-modules
提供了两种方式实现,一种是通过package.json
中的type
字段,另一种是输入通过传入参数--eval, --print or STDIN
添加一个--input-type
标示
package.json
type
字段
在你的项目package.json中添加'type':'module'字段,Node.js就会把项目中所有的.js文件当作ES模块
如果项目中的一些文件使用了CommonJS并且你不能立即转换它们,你可以把那些文件重命名为.mjs或者把它们放到一个子文件夹然后添加一个package.json
包含{ “type”: “commonjs” }
,这样那些.js文件会被当作CommonJS处理
当Node.js想要加载任何文件的时候,它将会查找文件当前目录下的package.json
文件,找不到的话将向上查找直到根目录。这种行为非常类似于babel的.babelrc
文件。这种新的方式允许nodejs使用package级别的元数据和配置,类似于babel和其它工具目前使用的样子
--input-type
flag
使用-—input-type=module
作为ES模块运行字符串输入(-—eval、-—print
或STDIN
)。—-input-type
的值为-—input-type=module
或—input-type=commonjs
。
.cjs扩展名
只有.mjs文件扩展名被当作ES模块,新的.cjs文件扩展名将被当作CommonJS模块。.cjs扩展名是当.mjs和.js当作es模块的时候,保留项目中的CommonJS文件用到的。
显式文件名
默认情况下在新的--experimental-modules
下,import语句中的文件扩展名是强制性的:import ‘./file.js’
并不同于 import ‘./file’
。但是,CommonJS风格的自动扩展名处理行为可以通过--es-module-specifier-resolution=node
来开启(默认是es-module-specifier-resolution=explicit
)。包名引入仍然不变,例如import fs from ‘fs
我们提供了--es-module-specifier-resolution=node
可选使用Commonjs风格扩展名和的解析。但是我们默认关闭了它,在我们去除--experimental-modules
之前,用于收集用户对于完全指定路径(fully specified paths)的反馈。 你可以在这里找到关于这个话题的讨论。
这种设计的主要原因是,通过我们提供的特定解决办法,去鼓励开发者们编写浏览器和node共享的代码
module.createRequireFromPath
CommonJS的全局变量(如require, exports, module, __filename, __dirname
)在ES模块中将不会定义。但是可以使用module.createRequireFromPath()
去创造一个commonJS require函数在ES模块上下文中使用
只能引入Javascript
以前的--experimental-modules
允许导入JSON语句和原生模块。现在这个已经删除了,默认不能导入JSON语句和原生模块,您可以使用module.createRequireFromPath()
来实现原先的功能。
实验性标记--experimental-json-modules
可以支持json文件的导入。我们正在用浏览器对这个特性进行标准化,并且Node.js希望我们的支持能与最终的标准保持一致。
其它也有正在进行的工作,以涵盖WASM和其他未来潜在的模块类型。Node.js以后将以符合规范的方式增加对这些模块类型的支持。
npm包中的ES模块代码
这是一项正在进行的工作,可能会发生变化。通过package.json
的main
字段类型指定入口文件(这个文件是ES模块)。你可以使用ES模块创建一个包。如果文件扩展名是.mjs或者package.json
包含'type':'module'
Node.js的话,Node.js将它作为ES模块加载。
目前,无法创建一个既可以通过require('pkg')
又可以import ‘pkg’
来使用的npm包。我们正在努力解决这一问题,并且可能有对上述内容的修改。特别是,Node.js可能会选择'main'
以外的字段来定义包的ES模块入口点。但我们意识到社区已经接受了'main'
字段,所以这也不太可能这样做因为很多包已经使用了module
去引入ES模块的javascript,但可能没有评估在node.js中使用(因为文件名的扩展缺省的,或者代码里面包含require
表达式)。在解决这个问题之前,请不要发布任何打算给Node.js使用的ES模块的npm包。
工作进展
上面所有的这些都是作为Node.js 12 --experimental-modules
的一部分。在我们的规划中,在 2019年10月 Node.js 12 达到 LTS 并移除--experimental-modules
标识之前,我们希望有一些潜在的改进:
- 加载器(Loader)功能。加载器正在进一步开发以支持进程隔离、多加载器和具有较低级别hook的多领域支持。在标记移除之前,--loader API仍然会有很大的变化。
- 双重的CommonJS/ES模块包。我们希望为包作者提供一种标准的方式来发布一个包,这个包既可以被
require
到CommonJS,也可以被import
到ES模块中。 - 更容易的require。使用
Module.createRequireFromPath()
包含许多的模版文件。我们希望提供一种更简单的方式在ES模块代码中进行require
- 包路径映射(原文:Package path maps).我们想要提供一种包内路径到文件的映射。例如允许像
import sdk from ‘some-service/sdk’
从‘some-service/sdk’
到./src/sdk/public-api.mjs
的。 - 自动入口点模块类型检查。这将提供一种方式来基于静态分析运行Javascript代码(CommonJS或ES模块)
就是这样!我们希望你喜欢这个新的--experimental-modules
,并期待您的反馈。模块团队的工作在github.com/nodejs/modu…公开。