從這個API能看到整個前端的縮影
大家好,我卡頌。
如果要從JS中找一個API作為整個前端的縮影,ESM規范中的import再合適不過了。
本文我們從這個API出發,來聊聊web的發展。
web的本質是開放
在所有JS?運行時中,web?是最開放的(緊隨其后的可能是deno?)。這一點可以從import語法的「模塊說明符」窺探一絲端倪。
在ES規范中只明確「模塊說明符是一個字符串字面量」,并沒有限制「如何解析模塊說明符」,所以「解析模塊說明符」的任務就交給了宿主環境。
在web?的HTML規范中,「模塊說明符」可以是如下形式:
- 絕對路徑的url,比如:
- 以/、./、../開頭的相對路徑,比如:
- 定義模塊名到url的映射,再以模塊名的形式引入,比如:
再引入模塊:
PS:這種方式被稱為import-maps[1],當前瀏覽器兼容性還不高:
可以發現,這三種方式對「模塊說明符」的來源都很開放。反觀??Node.js?
?運行時,如果以包名的形式引入模塊,比如:
背后是一套指向node_modules,并最終指向npm庫的機制。npm是家私人公司,被github收購,而github被微軟收購。
所以,如果某一天國內無法直接安裝npm包,也不必驚訝,畢竟他的背后是一家私人公司。
與之相對,web的開放讓他不會面臨這種囧境。
兼容性的迭代
web?的發展史,就是一部「新三年、舊三年、修修補補又三年」的兼容史。很多API?的兼容性問題可以通過polyfill解決。
所以,很自然的,庫作者在面對模塊規范的兼容性問題時,也想替用戶做到最好。但是,這份努力也讓代碼行為變得更撲朔迷離。
比如:在ESM?模塊中是可以引入CJS?模塊的。對于如下CJS模塊:
在同級的ESM模塊中引入,并通過解構或者對象方法來使用hello:
為什么不能直接以「具名引入」的形式使用hello呢:
這是因為ESM規范的導入聲明都是靜態的,而CJS規范的導出是動態的,所以當ESM模塊引入CJS模塊時,在編譯時是沒法知道有哪些導出的。
這很符合規范,但看起來有點不符合直覺。
比如,React只提供了CJS規范的包,所以在ESM模塊中正確的引入方式是:
而大家日常開發顯然下面這種方式用的更多:
之所以這么引入不會報錯,是因為庫作者(比如vite、babel)在編譯時默默做了轉換。
為了方便開發者而違背規范,這其實是個很不好的事(類似的事還有npm、yarn的影子依賴)。
但開發者喜聞樂見的API就是好API,整個web的發展就是修修補補螺旋向上的。
bundle將長期存在
在vite橫空出世,帶來極致的開發時速度后,社區就掀起一股「bundle vs bundleless」的討論。
既然bundleless能為開發環境帶來提速,同樣的優勢能不能也帶到生產環境?或者更極端點,未來前端會逐漸拋棄打包工具么?
從ESM?規范的角度出發,答案是否定的。有兩個剛需現階段bundleless還無法解決:
- tree shaking
- ESM模塊過多,導致發起大量請求
所以,在未來很長一段時間內,打包工具仍會存在。
總結
在我的技術群中,經常看到新人前端發出感嘆:「不知道該學啥」。
究其原因,當前的前端開發,主要是使用「集成了最佳實踐的各種大型框架」(比如Nuxt?、Next.js?、UmiJS...)。而這些封裝完備的框架為了降低上手門檻,隱藏了大量技術細節。
如果你也有這種迷茫,我建議你從ESM規范開始學起。
他就像一張地圖,能夠串聯起前端的方方面面。
參考資料
[1]import-maps:https://github.com/WICG/import-maps