App下載

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

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

文章來源于公眾號:驚天碼盜 ,作者:雞湯小弟

前言

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

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

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

瀏覽器與js

瀏覽器在渲染頁面前會解析 HTML文檔,通過 HTML 解析器輸出 DOM 元素和屬性。

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

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

瀏覽器與js

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

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

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

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

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

js與模塊化

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

起初,CommonJS專攻服務(wù)端,原名叫 ServerJS,后來,為了統(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í)候就必須指明所依賴的模塊,然后把本模塊的代碼寫在回調(diào)函數(shù)里。模塊的加載也是通過下載-回調(diào)這樣得到過程來進(jìn)行,這個(gè)思想也是\A******MD****的基礎(chǔ)(https://github.com/amdjs/amdjs-api/wiki/AMD)。
  • 最后一波人,既不想丟掉舊的規(guī)范,也不想像AMD那樣重來。他們認(rèn)為CommonJs的一些理念是好的,比如通過require來聲明依賴,新的規(guī)范應(yīng)兼容這些,同時(shí)也認(rèn)為AMD也有可取之處,比如預(yù)加載等。最終他們制定來一個(gè)*Modules/Wrappings*規(guī)范(http://wiki.commonjs.org/wiki/Modules/Wrappings)。

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

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

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

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

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

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

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

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

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

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

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

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

一切皆為模塊

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

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

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

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

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

webpack與vite

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

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

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

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

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

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

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

0 人點(diǎn)贊