Node.js 12中新的ECMAScript模块支持(译)

栏目: Node.js · 发布时间: 7年前

内容简介:本文于2019-04-23由Dr. Axel博士发表在个人网站2ality,全文对Node.js12中模块的新特性做了详细的分析解读。Node.js 12(于2019-04-23发布)为ECMAScript模块带来了改进的支持。它实施了去年年底发布的计划的第二阶段。目前,这种支持可以在继续阅读以了解这种对ECMAScript模块的新支持是如何工作的。

本文于2019-04-23由Dr. Axel博士发表在个人网站2ality,全文对Node.js12中模块的新特性做了详细的分析解读。

Node.js 12(于2019-04-23发布)为ECMAScript模块带来了改进的支持。它实施了去年年底发布的计划的第二阶段。目前,这种支持可以在 experimental-modules 获得。

继续阅读以了解这种对ECMAScript模块的新支持是如何工作的。

简要说明:文件扩展名.mjs更方便,但.js也可以为ES模块启用。

1.本篇文章中使用的术语和缩写

  • CommonJS模块(CJS):指的是原始的Node.js模块标准。
  • ECMAScript模块(ES模块,ESM):指通过ECMAScript规范标准化的模块。
  • package.prop是指prop的package.json。

2.模块说明符

模块说明符是标识模块的字符串。它们在CommonJS模块和ES模块中的工作方式略有不同。在我们查看差异之前,我们需要了解模块说明符的不同类别。

2.1 模块说明符的类别

在ES模块中,我们区分以下类别的说明符。这些类别源自CommonJS模块。

  • 相对路径:以点开头。例:
./some/other/module.mjs
../../lib/counter.mjs
复制代码
  • 绝对路径:以斜杠开头。例:
/home/jane/file-tools.mjs
复制代码
  • URL:包括协议(从技术上讲,路径也是URL)。例:
'https://example.com/some-module.mjs'
'file:///home/john/tmp/main.mjs'
复制代码
  • 裸路径:不以点,斜杠或协议开头,并且由没有扩展名的单个文件名组成。例子:
'lodash'
'the-package'
复制代码
  • 深度导入路径:以裸路径开始,至少有一个斜杠。例:
'the-package/dist/the-module.mjs'
复制代码

2.2 CommonJS模块说明符

这是CommonJS处理模块说明符的方式:

  • CommonJS不支持URL作为说明符。
  • 相对路径和绝对路径按预期处理。
  • 您可以将目录foo作为模块加载:
    • 如果有文件 foo/index.js
    • 如果存在foo/package.json其属性"main"指向模块文件的文件。
  • 根据node_modules找到的目录解析裸路径和深度导入路径:
    • 与导入模块位于同一目录中
    • 在该目录的父级中
    • 其它。
  • 如果说明符X未引用文件,则系统会尝试使用说明符 X.jsX.jsonX.node

此外,CommonJS模块可以访问两个特殊的模块全局变量:

  • __filename:包含当前模块的路径。
  • __dirname:包含当前模块的父目录的路径。

本节的来源:Node.js文档的“模块”页面。

2.3 Node.js中的ES模块说明符

  • 除裸路径外,所有说明符都必须引用实际文件。与CommonJS相比,ESM不会添加缺少的文件扩展名。
  • 仅file:支持URL说明符的协议。
  • 目前不支持绝对路径。作为解决方法,您可以使用以file:///。开头的URL 。
  • 相对路径在Web浏览器中被解析 - 相对于当前模块的路径。
  • 裸路径相对于node_modules目录进行解析。裸路径引用的模块通过package.main(类似于CJS)指定。
  • 深度导入路径也相对于node_modules目录进行解析。
  • 不支持导入目录。换句话说,既不能使用package.main(只能用于packages)也不能通过index.*工作。

所有内置的Node.js模块都可以通过裸路径获得,并命名为ESM导出。例如:

import * as path from 'path';
import * as assert from 'assert';

assert.equal(
  path.join('a/b/c', '../d'), 'a/b/d');
复制代码

2.4 文件扩展名

Node.js支持以下默认文件扩展名:

  • mjs 用于ES模块

  • cjs 用于CommonJS模块 文件扩展名.js代表ESM或CommonJS。它是哪一个,取决于package.type,它有两个设置:

  • commonjs(默认值):扩展名为.js或没有扩展名的文件被解析为CommonJS。

    "type": "commonjs"
    复制代码
  • module:具有扩展名.js或没有扩展名的文件被解析为ESM。

    "type": "module"
    复制代码

要查找package.json给定文件,Node.js将在与文件,父目录等相同的目录中进行搜索。

2.5将非文件源代码解释为CommonJS或ESM

并非所有Node.js执行的源代码都来自文件。你也可以通过stdin --eval和它发送代码--print。命令行选项--input-type允许您指定如何解释此类代码:

  • 作为CommonJS(默认): --input-type=commonjs
  • 作为ESM: --input-type=module

3. 互操作能力

3.1 从ESM导入CommonJS

目前,有两种从ES模块导入CommonJS模块的选项。

考虑以下CommonJS模块。

// common.cjs
module.exports = {
  foo: 123,
};
复制代码

第一个选项是默认导入它(将来可能会添加对命名导入的支持):

// es1.mjs
import * as assert from 'assert';

import common from './common.cjs'; // default import
assert.equal(common.foo, 123);
复制代码

第二种选择是使用createRequire():

// es2.mjs
import * as assert from 'assert';

import {createRequire} from 'module';
const require = createRequire(import.meta.url);

const common = require('./common.cjs');
assert.equal(common.foo, 123);
复制代码

3.2 从CommonJS导入ESM

如果要从CommonJS模块导入ES模块,可以使用该import()运算符。

例如,采用以下ES模块:

// es.mjs
export const bar = 'abc';
复制代码

这里我们从CommonJS模块导入它:

// common.cjs
const assert = require('assert');

async function main() {
  const es = await import('./es.mjs');
  assert.equal(es.bar, 'abc');
}
main();
复制代码

4. 各种其他功能

4.1 import.meta.url

鉴于 __filename和 __dirname在ES模块不可用,我们需要一个替代方案。import.meta.url是另一种选择。它包含file:具有绝对路径的URL。例如:

'file:///Users/rauschma/my-module.mjs'
复制代码

注意:url.fileURLToPath()用于提取路径 - new URL().pathname并不是总可以正常运行:

import * as assert from 'assert';
import {fileURLToPath} from 'url';

//::::: Unix :::::

const urlStr1 = 'file:///tmp/with%20space.txt';
assert.equal(
  new URL(urlStr1).pathname, '/tmp/with%20space.txt');
assert.equal(
  fileURLToPath(urlStr1), '/tmp/with space.txt');

const urlStr2 = 'file:///home/thor/Mj%C3%B6lnir.txt';
assert.equal(
  new URL(urlStr2).pathname, '/home/thor/Mj%C3%B6lnir.txt');
assert.equal(
  fileURLToPath(urlStr2), '/home/thor/Mjölnir.txt');

//::::: Windows :::::

const urlStr3 = 'file:///C:/dir/';
assert.equal(
  new URL(urlStr3).pathname, '/C:/dir/');
assert.equal(
  fileURLToPath(urlStr3), 'C:\\dir\\');
复制代码

下一节演示使用import.meta.url和url.fileURLToPath()。

反过来想url.fileURLToPath()是url.pathToFileURL():它将路径转换为文件URL。

4.2 fs.promises

fs.promises包含fsAPI 的promisified版本并按预期工作

import {fileURLToPath} from 'url';
import {promises as fs} from 'fs';

async function main() {
  // The path of the current module
  const pathname = fileURLToPath(import.meta.url);
  const str = await fs.readFile(pathname, {encoding: 'UTF-8'});
  console.log(str);
}
main();

复制代码

4.3 --experimental-json-modules

使用该标志--experimental-json-modules,Node.js将.json文件加载为JSON。

以JSON文件为例data.json:

{
  "first": "Jane",
  "last": "Doe"
}
复制代码

可以从ES模块导入它,如下所示(如果同时使用ESM和JSON模块的标志):

import * as assert from 'assert';
import data from './data.json';

assert.deepEqual(
  data,
  {first: "Jane", last: "Doe"});
复制代码

5.npm上的ES模块

目前,npm上的ES模块引用有以下两种方式可以选择:

require('mylib')
import from 'mylib

你不能两者兼顾(深度导入路径是合理的解决方法)。我们正在努力改变这一状况。它可能会通过package.main更强大的功能来完成。

在该功能准备就绪之前,对处理该功能的人员有以下要求:

“在解决之前,请不要发布任何供Node.js使用的ES模块包。”
复制代码

6.在Node.js上使用ES模块

从Node.js 12开始,Node.js上使用ES模块有以下选项:

  • library:ESM由John-David Dalton维护。esm还支持旧版本的Node.js.

  • Flag--experimental-modules

当Node.js 12达到LTS状态时,可能会在2019年10月删除ESM支持的标志。

最后欢迎大家关注公众号前端小苑,我会定期在这里发表原创文章。

Node.js 12中新的ECMAScript模块支持(译)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Game Programming Patterns

Game Programming Patterns

Robert Nystrom / Genever Benning / 2014-11-2 / USD 39.95

The biggest challenge facing many game programmers is completing their game. Most game projects fizzle out, overwhelmed by the complexity of their own code. Game Programming Patterns tackles that exac......一起来看看 《Game Programming Patterns》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具