2017-09-12: Node.js v8.5.0 發布,在這個版本中,正式以實驗性的方式支持 ES6 Module。
長久以來,ES6+ 規范 v8 已經支持了很多了,在圖表中我們可以看到, Node 已經支持了 97% 的新語法了。那么剩下的還未支持的有誰呢?ES Module 就在其中,也就是 import
/export
語法。
首先我們先簡單講解一下為什么 ES Module 到現在還未支持?
首先,ES6 語法其實基本都是一些語法糖,所以 v8 支持起來是很簡單的事情。但是對于 ES Module 來說,如果你簡單理解,確實是語法糖,但是ES Module不僅僅是你想的那么簡單。
在這里,我們將 Node 原始的模塊方式 CommonJS 簡稱為 CJS,而 ES Module 稱為 ESM。
- CJS 采用的是動態同步加載,也就是說運行的時候確定加載的文件,很明顯這樣做有一個好處就是靈活,但是缺點就是無法很好的處理循環引用的問題。而且是同步加載,這會導致加載速度過慢。
- ESM 采用的是靜態異步加載,最大的區別便是采用了靜態分析。大家都知道
import
必須要寫在文件的頂層,這也就是為了能夠靜態分析你需要加載的模塊。首先他能很好的解決循環依賴的問題。
其次是異步加載。在 CJS 中,JS 的加載是同步進行的,也就是說我必須要等待上一個 JS 加載完成,才能夠加載下一個 JS,大家也懂得,這樣很明顯浪費了 Node 異步的有點。這也就會導致如果 JS 文件過多,系統的啟動時間會被大大加長。
更多的區別請查看這里
由于 ES Module 和 CJS 的區別有一些巨大,所以才導致兼容起來比較困難。
說了這么多,那么我們來說一下如何使用呢?
- 安裝 Node.js v8.5.0
- 創建全新的文件類型
index.mjs
文件。 - 隨意書寫一段代碼
import os from 'os'
console.log(os.cpus())
- 添加啟動參數
----experimental-modules
運行代碼。node --experimental-modules index.mjs
.mjs
是什么文件呢?很明顯因為 ESM 和 CJS 的加載方式不同,為了更高的區分這兩種不同的加載方式,于是創建了.mjs
(Module JavaScript)。.mjs
就是表示當前文件用 ESM 的方式進行加載,如果是普通的.js
文件,則采用 CJS 的方式加載。
關于 ES6 Module 的內容我就不多講了,更多的請看 阮一峰 大牛的 ES6 教程。
注意事項
ESM 都是在嚴格模式下運行的,也就是說如果以直接使用一個會定義的變量的話,會導致程序報錯。
所有的模塊路徑都是支持 URL 的,也就是說你可以通過
import ('./a?query=some')
導入模塊,會通過內置的 URL 進行解析。就目前來看,只支持file:
協議。
與 ES Module 的異同
- 暫時不支持動態加載,當然很快也就會支持了,究竟有多快呢,我也不知道。
- 所有的模塊都不在具有文件元信息,比如
__dirname
和__filename
。不過這里有一個提議(proposal),就是通過import.meta
獲取相關的文件元信息。但是如果你是想獲取__dirname
的話,可以通過下面這種方式
// dirinfo.js
module.exports = {__dirname}
// index.mjs
import dirinfo from './dirinfo'
console.log(dirinfo.__dirname)
ESM 和 CJS 的交互
ESM 可以導入 CJS 的模塊。導入之后 CJS 默認都是有一個
default
的,也就是module.exports
。現在暫時還不支持選擇性導入,import {a, b} from './cjs'
。但是支持全部導入import * as cjs from 'cjs'
,此時cjs
中會包含default
。-
你不能在 ESM 中使用
require()
,原因有以下幾條:- 模塊路徑的處理方式有一些不同: ESM 是不支持
NODE_PATH
和require.extensions
。因為 ESM 是使用 URL 的方式,所以會產生一些區別,所以不能使用require()
。 - ESM 總是異步加載,我們上文也說過了。采用異步加載的原因也是為了盡可能和瀏覽器保持最大的兼容性。現在來看,這種方式和 CJS 的同步加載方式會有一些不太兼容。
- 禁止同步的模塊加載也是為了給以后頂級使用 await 預留一定的余地。
- 模塊路徑的處理方式有一些不同: ESM 是不支持
如何在舊版本的 Node 中使用 ES Module 呢?
方法一:使用 Babel
方法二:請查看 @std/esm by John-David Dalton.
One more thing
- 預計會在 Node10 LTS 的時候可以默認啟用參數,也就是說 Node9 的話,我們還是要添加相應的啟動參數的。
-
.mjs
文件后綴名對于 Node 來說是非常重要的,但是如果對于瀏覽器來說,僅僅取決于 Media-Type,后綴名無關緊要。