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