本文於2019-04-23由Dr. Axel博士發表在我的網站2ality,全文對Node.js12中模塊的新特性作了詳細的分析解讀。html
Node.js 12(於2019-04-23發佈)爲ECMAScript模塊帶來了改進的支持。它實施了去年年末發佈的計劃的第二階段。目前,這種支持能夠在experimental-modules
得到。前端
繼續閱讀以瞭解這種對ECMAScript模塊的新支持是如何工做的。node
簡要說明:文件擴展名.mjs更方便,但.js也能夠爲ES模塊啓用。npm
模塊說明符是標識模塊的字符串。它們在CommonJS模塊和ES模塊中的工做方式略有不一樣。在咱們查看差別以前,咱們須要瞭解模塊說明符的不一樣類別。json
在ES模塊中,咱們區分如下類別的說明符。這些類別源自CommonJS模塊。promise
./some/other/module.mjs
../../lib/counter.mjs
複製代碼
/home/jane/file-tools.mjs
複製代碼
'https://example.com/some-module.mjs'
'file:///home/john/tmp/main.mjs'
複製代碼
'lodash'
'the-package'
複製代碼
'the-package/dist/the-module.mjs'
複製代碼
這是CommonJS處理模塊說明符的方式:瀏覽器
X.js
,X.json
和X.node
。此外,CommonJS模塊能夠訪問兩個特殊的模塊全局變量:bash
本節的來源:Node.js文檔的「模塊」頁面。async
全部內置的Node.js模塊均可以經過裸路徑得到,並命名爲ESM導出。例如:網站
import * as path from 'path';
import * as assert from 'assert';
assert.equal(
path.join('a/b/c', '../d'), 'a/b/d');
複製代碼
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將在與文件,父目錄等相同的目錄中進行搜索。
並不是全部Node.js執行的源代碼都來自文件。你也能夠經過stdin --eval和它發送代碼--print。命令行選項--input-type容許您指定如何解釋此類代碼:
目前,有兩種從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);
複製代碼
若是要從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();
複製代碼
鑑於 __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。
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();
複製代碼
使用該標誌--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"});
複製代碼
目前,npm上的ES模塊引用有如下兩種方式能夠選擇:
require('mylib')
import from 'mylib
你不能二者兼顧(深度導入路徑是合理的解決方法)。咱們正在努力改變這一情況。它可能會經過package.main更強大的功能來完成。
在該功能準備就緒以前,對處理該功能的人員有如下要求:
「在解決以前,請不要發佈任何供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支持的標誌。
最後歡迎你們關注公衆號前端小苑,我會按期在這裏發表原創文章。