原文:Announcing a new --experimental-modules
在2017年,Node.js 8.9.0釋出了對ECMAScript模組的實驗性支援。這種ECMAScript模組的支援是需要在後面加上--experimental-modules
標識來執行。
現在,主流瀏覽器都能通過<script type=”module”>
標籤支援ECMAScript 模組 (ES modules) 。各種專案npm包都使用了ES模組編寫,並且可以通過<script type= " module " >
在瀏覽器中直接使用。支援匯入對映(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…公開。