Node.js 12中新的ECMAScript模塊支持(譯)

本文於2019-04-23由Dr. Axel博士發表在我的網站2ality,全文對Node.js12中模塊的新特性作了詳細的分析解讀。html

Node.js 12(於2019-04-23發佈)爲ECMAScript模塊帶來了改進的支持。它實施了去年年末發佈的計劃的第二階段。目前,這種支持能夠在experimental-modules得到。前端

繼續閱讀以瞭解這種對ECMAScript模塊的新支持是如何工做的。node

簡要說明:文件擴展名.mjs更方便,但.js也能夠爲ES模塊啓用。npm

1.本篇文章中使用的術語和縮寫

  • CommonJS模塊(CJS):指的是原始的Node.js模塊標準。
  • ECMAScript模塊(ES模塊,ESM):指經過ECMAScript規範標準化的模塊。
  • package.prop是指prop的package.json。

2.模塊說明符

模塊說明符是標識模塊的字符串。它們在CommonJS模塊和ES模塊中的工做方式略有不一樣。在咱們查看差別以前,咱們須要瞭解模塊說明符的不一樣類別。json

2.1 模塊說明符的類別

在ES模塊中,咱們區分如下類別的說明符。這些類別源自CommonJS模塊。promise

  • 相對路徑:以點開頭。例:
./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模塊能夠訪問兩個特殊的模塊全局變量:bash

  • __filename:包含當前模塊的路徑。
  • __dirname:包含當前模塊的父目錄的路徑。

本節的來源:Node.js文檔的「模塊」頁面。async

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支持的標誌。

最後歡迎你們關注公衆號前端小苑,我會按期在這裏發表原創文章。

相關文章
相關標籤/搜索