在Nodejs或?yàn)g覽器運(yùn)行ESM代碼

前言

經(jīng)典面試題目就是《Common.js和ES module的區(qū)別》,這一題很多人都會熟練地背出答案。

Commonjs

  • CommonJs可以動態(tài)加載語句,代碼發(fā)生在運(yùn)行時(shí)
  • CommonJs導(dǎo)出值是拷貝, 不好排查引起變量污染

ES module(后續(xù)簡稱esm)

esm是JavaScript模塊化的未來。因?yàn)樗鉀Q了變量污染,代碼維護(hù),代碼依賴的問題。它讓你的代碼更加科學(xué)。這也是deno默認(rèn)采用esm的原因。

回歸正題,我們有什么方法在Nodejs或者瀏覽器直接運(yùn)行esm代碼,這是個(gè)有趣而又實(shí)際的問題。

如何在Nodejs環(huán)境允許

1.利用編譯工具運(yùn)行esm

最常見的方式是利用webpack等打包工具搭配babel使用。隨著webpack和vue的大熱,這些工具似乎成為了標(biāo)配,但是webpack的缺點(diǎn)也很明顯,它能讓commonjs和esm的混寫,導(dǎo)致代碼存在一些寫法不規(guī)范的情況,我相信這種情況是普遍出現(xiàn)在業(yè)務(wù)代碼里面,也存在于antd3這樣的知名第三方組件庫中。

rollup則是基于ES6的語法規(guī)范進(jìn)行編譯,它的輕便小巧,非常適合npm庫的打包。新興的打包工具例如esbuildswc,也可以實(shí)現(xiàn)編譯打包,即使速度越來越快,但是還是需要編譯的過程。這些倉庫很重要的一個(gè)特點(diǎn)就是使用esm語法。

以上這些工具都可以應(yīng)用于esm語法編譯,但是有很多項(xiàng)目不一定需要打包編譯這樣耗時(shí)的流程的,例如一些cli工具、簡易微服務(wù)等,如何保證高效正確的運(yùn)行esm代碼呢?

2. 利用第三方庫運(yùn)行esm

在Nodejs版本較低的情況,我們可以利用一些工具,工具的使用形式有幾種,一種是Module Loader,另一種是Command Line(簡稱為cli)。

Module Loader,這里介紹standard-things/esm,它可以preload第三方提供的esm包,從此,可以做到babelless, bundleless。你不需要使用大型編譯工具也可以直接運(yùn)行esm代碼,使用方式如下。

node -r esm index.js

同樣,egoist/esbuild-register這個(gè)庫在esbuild的支持下,同樣可以做到Module Loader的效果,利用esbuild的高性能特性,代碼運(yùn)行效率更高。

node -r esbuild-register index.js

Command Line,基于封裝后的cli,不過是換一種形式進(jìn)行模塊的提前處理。babel-node直接利用它的babel語法優(yōu)勢來運(yùn)行esm代碼。由于babel本身還是js的實(shí)現(xiàn),它的官方文檔也表明了不建議在生產(chǎn)環(huán)境使用,會導(dǎo)致內(nèi)存高占用的問題,這也是這一類工具的通病。

babel-node index.js

同樣,esno可以直接在命令行運(yùn)行esm代碼。原理基于esbuild。在這里更推薦使用這種方式,鑒于esbuild是由go語言實(shí)現(xiàn),能夠較大程度解決內(nèi)存高占用的問題,保證了一定的執(zhí)行性能。

esno index.ts
esmo index.ts

這一類第三方倉庫適合在低版本nodejs且非生產(chǎn)環(huán)境使用,它們的存在是為了便利性,而并非實(shí)用性和穩(wěn)定性。怎么樣才能高效地運(yùn)行esm代碼?

3.Native Nodejs運(yùn)行esm

Node verison 13.2.0 起開始正式支持 ES Modules 特性

所以利用Native Nodejs環(huán)境運(yùn)行esm代碼是非常必要的,高版本的Nodejs提供了直接運(yùn)行esm的功能,這里建議使用lts14版本。有兩種方式運(yùn)行esm代碼:

第一種,package.json中填寫type: "modules",表明模塊的類型。此后,直接運(yùn)行node index.js即可。

// pakage.json
{
  ...
  "type": "modules"
}

第二種,則是將文件名改成.mjs,標(biāo)明該文件是esm代碼。這兩種方式最大的區(qū)別則是模塊作用域。前者是包的作用域,它的聲明是以package為維度。后者則是以文件為維度,不受限于包的作用域。

如何在瀏覽器運(yùn)行esm

瀏覽器script type="module"兼容性

瀏覽器的情況有別于Nodejs環(huán)境,在大部分的新版本瀏覽器都支持esm的運(yùn)行。esm 級別的代碼編譯和打包,可以有效地減少包的體積和資源傳輸速度。這也是為什么像 vite 這樣的框架會采用現(xiàn)代瀏覽器的打包模式(外加legacy兼容模式)的原因。具體的原理是在 html 當(dāng)中的 script 標(biāo)簽加入type="module" 則表明它引入的是esm代碼,當(dāng)舊瀏覽器沒法支持esm的情況下,它會讀取nomodule script中的地址,讀取兼容版本的 js 代碼。這樣一來,可以有效地減少大部分瀏覽器加載的 js 體積,又保證了老瀏覽器的兼容性問題。

<script type="module" src="dist/index.js"></script>
<script nomodule src="dist/index.legacy.js"></script>

總結(jié)

如今Nodejs和瀏覽器環(huán)境都能對esm語法有了很好的 Native 支持。作為前端工程師的我們,應(yīng)該要保持著技術(shù)的前瞻性,在寫一個(gè)倉庫的時(shí)候,我們要想到要用typescript,esm還是common.js呢?為什么我們不選擇比較新的 js 運(yùn)行環(huán)境,迎接Javascript的第三個(gè)時(shí)代,參考《ESM Import與Bundleless》。

參考資料

2020年我們可以在Node中使用ES Modules了嗎

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容