Skip to content

Instantly share code, notes, and snippets.

@miaulightouch
Created August 16, 2018 15:58
Show Gist options
  • Save miaulightouch/c9f0b889c005542e8e7209fd159d09b2 to your computer and use it in GitHub Desktop.
Save miaulightouch/c9f0b889c005542e8e7209fd159d09b2 to your computer and use it in GitHub Desktop.

從 CommonJS module 到 ES module(上):require 與 import/export

自 webpack 將 import/export 列為最佳 module 語法的同時,搭配 babel 將 ES6 語法轉譯成 ES5,ES6 語法受到廣泛的推崇。

現在好像感覺用了 ES6 就能打遍天下無敵手,考試都能考一百分……

真的是這樣嗎?

接下來,我要來說說我將傳統 CommonJS module(以下簡稱 CJS)轉換到 ES module(以下簡稱 ESM)時的艱難與辛苦。


說在前頭,ESM 就算到了 nodejs 環境,目前仍然是次等公民。

尤其是對雙棲(node 與 browser)的 module 來說,更是個莫大的痛苦。

ESM 仍是試驗功能

根據 nodejs文件 指出,在執行 nodejs 時,後面還得掛上 --experimental-modules

在不做任何設定的前提下,符合 ESM 規範的程式附檔名必須為 .mjs 而非 .js。如:node --experimental-modules my-app.mjs

若透過 node --experimental-modules 啟動 REPL 模式的話,還會發現似乎不能手動輸入 import 指令。

現在導入 ESM 或許言之過早。

開始動工

這次轉移的目標是熱門的偵錯訊息套件 debug ,不過他在 nwjs 上有點問題,我就作為練習目標 fork 了一份,依 ES6 語法重寫並改名為 debug-es。

首先,在 node.js 上看到程式載入了內建的 tty, util 套件

https://gist.github.com/c7ff357bec318ab5e64b4d422e212b86

因為是在檔案開頭的地方匯入,所以我們也可以直接將上述套件改寫成 ESM

https://gist.github.com/ffd9d6d1cdf80708cdd59874b030308f

這樣就好了嗎?錯了。

https://gist.github.com/caebb99e62be293e0c72b92a056de24c

接下來是 exports ,在 CJS 上 exports 是一個模組內的全域 object,但在 ESM 上並不存在。

這時可以考慮牽涉 exports 的部分重構,或者在前面加上 const exports = {} 來維持整個代碼結構。

Require 動態模組載入 v.s Import 靜態模組載入

https://gist.github.com/ada210719310e76db8c6a0bf61cf1cad

緊接而來的是一個包在 try-catch 內的 require。

在這邊 require 跟 import 就展現出彼此的差別。

  1. 由於 import/export 是靜態的,所以不能包裹在 if-else 等語法之內。
  2. require 是 blocking,直到模組載入完之前不會進行接下來的工作。
  3. 仍在 Stage-3 的 dynamic import 是非同步的 Promise ,不僅無法取代 require,還會使得邏輯判斷更難處理。

依照原設計 supports-color 是作為選用套件,手動安裝後就可以開啟多色輸出。

但若轉為 ESM 就只能考慮:

  1. 放棄 supports-color,把整段 try-catch 拔掉
  2. 保留 supports-color,並正式放入 dependencies

這邊我選擇後者,於是檔頭載入模組的部分就變成了

https://gist.github.com/a8dbf05a9ab56497f49c9e6cb21a543f

而原本 try-catch 地方抽掉,簡化原本的 if 判斷

https://gist.github.com/ad25e2b3a606a9e630b40e9b87b485fe

導出模組

在 CJS 中的 module.exports 可以直接對應到 ES6 的 export default

https://gist.github.com/9b5adab8197f38b71322bce22ef66c74

一樣,看到 require 就往檔頭放 import ,就會變成:

https://gist.github.com/41eaf7542356e618cacb4ec92c494827

小結

至此,基本上已經完成了轉換工作,不過畢竟從動態載入的 CJS 轉向 靜態的 ESM,其他關連的模組可能會因此影響執行方式,變得不得重新梳理結構。(如 index.js 的分支模組載入,也要整個重寫成 ESM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment