App下載

瀏覽器、ESM規(guī)范、模塊化、webpack和vite之間聯(lián)系?

猿友 2020-09-27 14:39:08 瀏覽數(shù) (4934)
反饋

文章來(lái)源于公眾號(hào):驚天碼盜 ,作者:雞湯小弟

前言

通俗的講:JS這門(mén)語(yǔ)言,在設(shè)計(jì)之處就沒(méi)有模塊體系,所以他會(huì)經(jīng)歷模塊化演變,直到2015年,TC39(一群瀏覽器廠商代表組成的委員會(huì))發(fā)布了ES6規(guī)范,ES Modules才被世人所知,也就是ESM。而在模塊化演變過(guò)程中,模塊化本身暴露的問(wèn)題也越來(lái)越明顯。項(xiàng)目中的模塊越來(lái)越多,在管理上造成了混亂,所以迫切需要一些工具解決各種模塊類型混亂的問(wèn)題。webpack和vite等工具就是用來(lái)解決這些問(wèn)題的。

從瀏覽器的發(fā)展,JS的流行,到模塊化探索,再到構(gòu)建工具的區(qū)別,這里面涉及的知識(shí)點(diǎn)很多,在這里簡(jiǎn)單的列幾點(diǎn):

  • 瀏覽器的運(yùn)行機(jī)制
  • 模塊化的演變
  • 模塊化工具的演變
  • webpack的演變
  • vite的誕生
  • 前端開(kāi)發(fā)者的選擇

瀏覽器與js

瀏覽器在渲染頁(yè)面前會(huì)解析 HTML文檔,通過(guò) HTML 解析器輸出 DOM 元素和屬性。

然后構(gòu)建 DOM tree 和 CSSOM tree。這兩個(gè)模型構(gòu)建時(shí)是相互獨(dú)立的。

最后 CSSOM tree 和 DOM tree 合并成渲染樹(shù),然后用于計(jì)算每個(gè)可見(jiàn)元素的布局,并輸出繪制流程,將像素渲染到屏幕上。

瀏覽器與js

如果 DOM 或 CSSOM 被修改,需要重新構(gòu)建,以確定哪些像素需要在屏幕上進(jìn)行重新渲染,這里會(huì)涉及到兩個(gè)知識(shí)點(diǎn):repeatreflow。

而 JS 允許查詢和修改 DOM 和 CSSOM,同樣 JS 也會(huì)阻塞 DOM 和 CSSOM 的構(gòu)建和渲染,所以我們要把 JS 放在 HTML 的底部加載。

現(xiàn)在的瀏覽器的內(nèi)核大多分為兩部分:渲染引擎JS引擎。

渲染引擎,負(fù)責(zé)對(duì)網(wǎng)頁(yè)語(yǔ)法的解釋并渲染網(wǎng)頁(yè)。我們常說(shuō)的gecko引擎、"斯巴阿"(edge)、presto引擎、webkit、blink引擎等都是渲染引擎。

JS引擎,是專用于對(duì)JS進(jìn)行解釋、編譯和執(zhí)行,使網(wǎng)頁(yè)達(dá)到動(dòng)態(tài)得到效果。常說(shuō)的V8引擎就是JS引擎。

js與模塊化

在 js 模塊化進(jìn)程中,CommonJS 常用在服務(wù)端,AMD、CMD規(guī)范常用在客戶端。

起初,CommonJS專攻服務(wù)端,原名叫 ServerJS,后來(lái),為了統(tǒng)一前后端而改名 CommonJs 。而就在社區(qū)討論下一版規(guī)范的時(shí)候,內(nèi)部發(fā)生了比較大的分歧,分裂出三個(gè)主張,漸漸的形成三個(gè)不同的派別:

  • 在現(xiàn)有CommonJs的基礎(chǔ)上滿足瀏覽器的需要,把現(xiàn)有模塊化轉(zhuǎn)化為適合瀏覽器端即可。制定了*Modules/Transport*http://wiki.commonjs.org/wiki/Modules/Transport)規(guī)范,browserify就是這樣一個(gè)工具,可以把nodejs的模塊編譯成瀏覽器可用的模塊。
  • 還有一波人,認(rèn)為瀏覽器與服務(wù)器環(huán)境差別太大,不能沿用舊的標(biāo)準(zhǔn),而且瀏覽器必須異步加載代碼,那么模塊在定義的時(shí)候就必須指明所依賴的模塊,然后把本模塊的代碼寫(xiě)在回調(diào)函數(shù)里。模塊的加載也是通過(guò)下載-回調(diào)這樣得到過(guò)程來(lái)進(jìn)行,這個(gè)思想也是\A******MD****的基礎(chǔ)(https://github.com/amdjs/amdjs-api/wiki/AMD)。
  • 最后一波人,既不想丟掉舊的規(guī)范,也不想像AMD那樣重來(lái)。他們認(rèn)為CommonJs的一些理念是好的,比如通過(guò)require來(lái)聲明依賴,新的規(guī)范應(yīng)兼容這些,同時(shí)也認(rèn)為AMD也有可取之處,比如預(yù)加載等。最終他們制定來(lái)一個(gè)*Modules/Wrappings*規(guī)范(http://wiki.commonjs.org/wiki/Modules/Wrappings)。

RequireJs 的出現(xiàn),迅速被廣大開(kāi)發(fā)者接受。但是 RequireJs 也有被吐槽的地方,*預(yù)先下載沒(méi)有什么爭(zhēng)議,預(yù)先執(zhí)行是否需要*?如果一個(gè)模塊依賴十個(gè)其他模塊,那么在本模塊的代碼執(zhí)行之前,要先把其他十個(gè)模塊的代碼都執(zhí)行一遍,不管這些模塊是不是馬上會(huì)被用到。這個(gè)性能消耗是不不容忽視的。

針對(duì) RequireJs 的不優(yōu)雅的地方,國(guó)內(nèi)大牛玉伯寫(xiě)出了 SeaJs。SeaJs 全面擁抱 Modules/Wrapping 規(guī)范,但也沒(méi)有完全按照 Modules/Wrapping 規(guī)范,seajs 并沒(méi)有使用 declare 來(lái)定義模塊,而是使用和 requirejs 一樣的define。依賴會(huì)被預(yù)先下載,在需要執(zhí)行的時(shí)候執(zhí)行。同樣 seajs 也實(shí)現(xiàn)了在需要執(zhí)行的時(shí)候下載這一功能,提供了 require.async API,并支持 CommonJs 和 RequireJs 關(guān)于模塊對(duì)外暴露 API 的方式。鑒于 seajs 融合了太多的東西,已經(jīng)無(wú)法說(shuō)它遵循哪個(gè)規(guī)范了,所以玉伯干脆就自立門(mén)戶,起名曰CMD(Common Module Definition)規(guī)范。

以上所講的 CommonJs 、AMD、CMD 等等,只是社區(qū)制定而非官方。官方一看社區(qū)呼聲這么高,js模塊化終于在2015年發(fā)布ES6正式版。從此ESM誕生了。

ES6 模塊的設(shè)計(jì)思想是盡量的靜態(tài)化,使得編譯時(shí)就能確定模塊的依賴關(guān)系,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運(yùn)行時(shí)確定這些東西。比如,CommonJS 模塊就是對(duì)象,輸入時(shí)必須查找對(duì)象屬性。

CommonJs 是運(yùn)行時(shí)加載,ES6是編譯時(shí)加載??赡苣銓?duì)編譯和運(yùn)行這兩個(gè)關(guān)鍵詞有點(diǎn)不理解。CommonJs 是先加載模塊,輸出一個(gè)對(duì)象,輸出后內(nèi)部不會(huì)再變化。而 ES6 是語(yǔ)言層面的改變,它 輸出的是一個(gè)引用,提到引用就想到了JS的數(shù)據(jù)類型-引用類型 。等執(zhí)行的時(shí)候才會(huì)取值輸出。

模塊化與構(gòu)建工具

模塊的加載和傳輸,我們首先能想到兩種極端的方式:

  • 一種是每個(gè)模塊文件都單獨(dú)請(qǐng)求
  • 另一種是把所有模塊打包成一個(gè)文件然后只請(qǐng)求一次

顯而易見(jiàn),每個(gè)模塊都發(fā)起單獨(dú)的請(qǐng)求造成了請(qǐng)求次數(shù)過(guò)多,導(dǎo)致應(yīng)用啟動(dòng)速度慢;一次請(qǐng)求加載所有模塊導(dǎo)致流量浪費(fèi)、初始化過(guò)程慢。這兩種方式都不是好的解決方案,它們過(guò)于簡(jiǎn)單粗暴。

分塊傳輸,按需進(jìn)行懶加載,在實(shí)際用到某些模塊的時(shí)候再增量更新,才是較為合理的模塊加載方案。

要實(shí)現(xiàn)模塊的按需加載,就需要一個(gè)對(duì)整個(gè)代碼庫(kù)中的模塊進(jìn)行靜態(tài)分析、編譯打包的過(guò)程。

但是我們的項(xiàng)目不單單只有js啊!有圖片、樣式、字體、html模版等資源。在編譯的時(shí)候,要對(duì)整個(gè)代碼進(jìn)行靜態(tài)分析,分析出各個(gè)模塊的類型和它們依賴關(guān)系,然后將不同類型的模塊提交給適配的加載器來(lái)處理。所以構(gòu)建工具產(chǎn)生了。

關(guān)于構(gòu)建工具可以查看之前寫(xiě)的一篇文章《關(guān)于前端構(gòu)建工具的大雜燴》。

webpack脫穎而出是因?yàn)樗睦砟睿?strong>一切皆為模塊。

一切皆為模塊

關(guān)于webpack還有一個(gè)故事,Tobias Koppers是 Webpack 倉(cāng)庫(kù)創(chuàng)建者,Tobias 的網(wǎng)絡(luò)昵稱叫 sokra ,后面我們就叫 sokra,sokra 沒(méi)有寫(xiě)過(guò) web 頁(yè)面,這個(gè)就很有意思了,一個(gè)沒(méi)有寫(xiě)過(guò)web頁(yè)面的人發(fā)明了當(dāng)代web開(kāi)發(fā)的基石。

sokra 一開(kāi)始是寫(xiě) Java 的, Java 里面有個(gè)很出名的技術(shù)叫GWT(Google Web Toolkit),GWT 是把 Java 代碼轉(zhuǎn)換成JavaScript,也就是讓后端來(lái)寫(xiě)前端,本質(zhì)上也是在AST層面對(duì)代碼做一層轉(zhuǎn)換,Babel 也是干這件事的,但是 GWT 這門(mén)技術(shù)沒(méi)有流行起來(lái),后面 Google 也不推廣了。

GWT里面有個(gè)feature叫「code splitting」,于是他當(dāng)時(shí)給用來(lái)做前端項(xiàng)目 Bundle 的 node.js 庫(kù) modules-webmake 提了一個(gè) issue,希望他們能實(shí)現(xiàn),「code splitting」就是Webpack現(xiàn)在提供的主要功能,也是當(dāng)代前端的基石。

大多技術(shù)的產(chǎn)生都離不開(kāi)前人的經(jīng)驗(yàn),都是把原有的技術(shù),改巴改巴就成了一個(gè)新的技術(shù),所以說(shuō)「 造輪子 」是技術(shù)發(fā)展不可避免的一環(huán)。

 造輪子是技術(shù)發(fā)展不可避免的一環(huán)

webpack與vite

webpack 與 vite 本質(zhì)上的區(qū)別就在于「 按需加載 」。

webpack 無(wú)論怎么提倡按需加載,在 ESM 面前都是假的按需加載。因?yàn)樵?ESM 之前瀏覽器并不支持模塊化,上面我們聊的模塊化大多都是社區(qū)的產(chǎn)物,不是本身語(yǔ)言層面的支持。而 ESM 的誕生,標(biāo)志著js有了自己的模塊體系。

有一個(gè)很好的例子可以說(shuō)明,在 script 標(biāo)簽中添加type=“modules”,引入 js 文件,在 js 文件中直接使用 import 導(dǎo)入,瀏覽器是可以正常運(yùn)行的,在這里我們不需要依賴工具幫我們實(shí)現(xiàn)瀏覽器對(duì)模塊化的支持。

vite 內(nèi)部的實(shí)現(xiàn)是把其他資源都編譯成 js 文件,換句話就是,把圖片、樣式、字體、vue文件等等其他資源模塊,都轉(zhuǎn)化JS模塊,讓瀏覽器加載編譯。當(dāng)然你可能會(huì)想到 webpack 也會(huì)啊,這不正是技術(shù)的本性嗎。難道 vite 的誕生就不能借鑒其他構(gòu)建工具嗎。

當(dāng)然很多同學(xué)都會(huì)有個(gè)疑問(wèn):vite會(huì)取代webpack嗎?

不會(huì),vite 的發(fā)展才剛剛起步,而瀏覽器的多樣性也導(dǎo)致 ESM 規(guī)范落地沒(méi)有那么快。webpack 的社區(qū)、生態(tài)已經(jīng)非常完善,這不是 vite 一個(gè)剛出生的小孩可以媲美的,但是 vite 的未來(lái)是一定會(huì)比 webpack 更加強(qiáng)大,這是科技發(fā)展所必然經(jīng)歷的,一代更比一代強(qiáng)。

以上就是W3Cschool編程獅關(guān)于瀏覽器、ESM規(guī)范、模塊化、webpack和vite之間聯(lián)系?的相關(guān)介紹了,希望對(duì)大家有所幫助。

0 人點(diǎn)贊