深入理解浏览器原理和架构|硬核
本文用47張圖帶你了解「瀏覽器的發(fā)展史」、「瀏覽器的架構(gòu)」、「瀏覽器的基本原理」以及?「瀏覽器的其它小知識(shí)」
???? 正文開(kāi)始
瀏覽器的主要功能就是向服務(wù)器發(fā)出請(qǐng)求,在瀏覽器窗口中展示HTML文檔、PDF、圖片、視頻等網(wǎng)絡(luò)內(nèi)容。這些網(wǎng)絡(luò)資源的位置由用戶使用 URI(統(tǒng)一資源標(biāo)示符)來(lái)指定指定。
或許在大多數(shù)人眼中,瀏覽器是這樣的:
大多數(shù)人眼中的瀏覽器「一個(gè)展示前端,一個(gè)未知的中間層連接著網(wǎng)絡(luò)世界」;甚至,網(wǎng)絡(luò)世界也可以省略:一臺(tái)顯示器,一個(gè)神秘的幕后黑盒。
如果你是一個(gè)前端開(kāi)發(fā)者,甚至每天瀏覽器陪伴你度過(guò)的時(shí)光比女朋友陪伴你的都要久,想想那每一個(gè)令人“不是那么期待”的早晨,每一個(gè)爭(zhēng)分奪秒完成任務(wù)的黃昏,只有瀏覽器和編輯器一直是你忠實(shí)的伙伴。而「就連你一直離不開(kāi)的VS Code編輯器,甚至也與瀏覽器有著莫大的淵源」。
屏幕前的朋友,你熟悉自己身邊的那些人嗎,熟悉那些與你朝夕相伴的朋友嗎?也許熟悉,也許不,那么,你是否愿意花些時(shí)間來(lái)熟悉一下這個(gè)在大量時(shí)間里與你有著莫大交集的瀏覽器的內(nèi)心世界呢?
今天,我們就來(lái)一探究竟,走進(jìn)這個(gè)我們與網(wǎng)絡(luò)連接最緊密的中間地帶。全文行文結(jié)構(gòu)大概如下:
目錄結(jié)構(gòu)瀏覽器發(fā)展簡(jiǎn)史
瀏覽器的誕生與發(fā)展
也許你知道,第一款瀏覽器 —— WorldWideWeb,誕生于1990年。但是現(xiàn)代瀏覽器的雛形卻孕育于 1980s年代。
一位名叫蒂姆·伯納斯-李的英國(guó)科學(xué)家在 1980 年代初期創(chuàng)建了一個(gè)名為 Inquire 的計(jì)算機(jī)程序,當(dāng)時(shí)他在總部位于瑞士的歐洲核研究組織(CERN,以其法文字母表示)工作。該計(jì)劃旨在「使在 CERN 工作的許多不同個(gè)人更容易共享信息」。
1990年,第一款瀏覽器問(wèn)世于Tim Berners-Lee 在 CERN 工作期間。您可能想知道 Web 瀏覽器到底是什么,簡(jiǎn)而言之,它是一個(gè)計(jì)算機(jī)程序,其目的是顯示和檢索數(shù)據(jù)。使用分配給存儲(chǔ)在網(wǎng)絡(luò)服務(wù)器上的每個(gè)數(shù)據(jù)集(網(wǎng)頁(yè))的 URL,它可以做到這一點(diǎn)。所以這意味著「當(dāng)您在瀏覽器中輸入內(nèi)容時(shí),您實(shí)際上是在輸入地址」,瀏覽器將使用該地址來(lái)獲取您想要查看的信息。「瀏覽器的另一個(gè)關(guān)鍵功能是以易于理解的方式向您解釋和呈現(xiàn)計(jì)算機(jī)代碼」。
下圖簡(jiǎn)單羅列了截止2020年瀏覽器的發(fā)展簡(jiǎn)史:
Timeline_of_the_Web_Browsers早期比較有名、有意義的瀏覽器主要包括Erwise、ViolaWWW、Mosaic、Netscape Navigator:
The-Early-Browsers1990年瀏覽器誕生之后的故事,想必您已經(jīng)早有耳聞:
「NCSA Mosaic」,或簡(jiǎn)稱 Mosaic,是互聯(lián)網(wǎng)歷史上「第一個(gè)獲普遍使用和能夠顯示圖片的網(wǎng)頁(yè)瀏覽器」。它由伊利諾伊大學(xué)厄巴納-香檳分校的NCSA組織在1993年發(fā)表,并于1997年1月7日正式終止開(kāi)發(fā)和支持,這款瀏覽器在當(dāng)時(shí)大受歡迎。Mosaic的出現(xiàn),算是點(diǎn)燃了后期互聯(lián)網(wǎng)熱潮的火種之一。后來(lái) Netscape Navigator 瀏覽器的開(kāi)發(fā),聘用了許多原有的 Mosaic 瀏覽器工程師,但是沒(méi)有采用 Mosaic 網(wǎng)頁(yè)瀏覽器的任何代碼。而傳承網(wǎng)景瀏覽器代碼的后裔為Firefox瀏覽器。
Marc Andreesen 與同事 Jim Clark 于 1994 年成立了一家公司,當(dāng)時(shí) Mosaic 還是最流行的瀏覽器,它們計(jì)劃打造出一個(gè)比 Mosaic 更好的瀏覽器,占領(lǐng)市場(chǎng),讓他們變得富有,并改變歷史。他們的第一個(gè)瀏覽器被稱為 Mosaic Netscape 0.9,不久更名 Netscape。得益于 JavaScript(JavaScript誕生于1995年,它是Netscape的Brendan Eich 僅花費(fèi)十天設(shè)計(jì)實(shí)現(xiàn)的。) 和“partial-screen loading”(即使頁(yè)面未完全加載,用戶也可以開(kāi)始閱讀頁(yè)面上的詳細(xì)信息,這一個(gè)新概念極大地豐富了在線體驗(yàn))等功能,它很快成為市場(chǎng)領(lǐng)導(dǎo)者,占據(jù)了瀏覽器市場(chǎng)上一半的份額,最瘋狂的時(shí)候,網(wǎng)景瀏覽器的市場(chǎng)份額接近百分之九十。
1995年8月9日,網(wǎng)景公開(kāi)募股,最初的價(jià)格是14美元一股,但后來(lái)陰差陽(yáng)錯(cuò),改為28美元一股發(fā)行,當(dāng)天收盤(pán)時(shí),網(wǎng)景的股票成了75美元一股,網(wǎng)景成為了當(dāng)時(shí)世界上市值最高的互聯(lián)網(wǎng)公司,Netscape 的 IPO 也助長(zhǎng)了日益增長(zhǎng)的網(wǎng)絡(luò)泡沫。
Netscape 最初的成功向那些在計(jì)算機(jī)和互聯(lián)網(wǎng)領(lǐng)域工作的人證明時(shí)代已經(jīng)永遠(yuǎn)改變了,這讓當(dāng)時(shí)業(yè)內(nèi)最強(qiáng)大的參與者感到震驚,一家名為 Microsoft 的西雅圖公司就是其中之一。計(jì)算機(jī)將通過(guò)瀏覽器運(yùn)行,瀏覽器可以在任何機(jī)器上運(yùn)行,從而使軟件行業(yè)民主化并降低其相當(dāng)大的進(jìn)入壁壘,這導(dǎo)致許多人猜測(cè)「操作系統(tǒng)的時(shí)代已經(jīng)結(jié)束」。Netscape 對(duì)微軟來(lái)說(shuō)是一個(gè)挑戰(zhàn),微軟在 1990 年代后期創(chuàng)建了自己的瀏覽器 Internet Explorer,當(dāng)時(shí)的IE和現(xiàn)在一樣,通常被視為劣質(zhì)產(chǎn)品。由于「微軟已經(jīng)建立了銷售其專有操作系統(tǒng) Windows 的帝國(guó)」,因此將這種由 Netscape 等公司帶頭的發(fā)展視為一種威脅。微軟通過(guò)對(duì)其產(chǎn)品的大量投資,使其與 Netscape 一樣好,成功地迅速扭轉(zhuǎn)了瀏覽器行業(yè)的局面。Windows 計(jì)算機(jī)在發(fā)布時(shí)已經(jīng)安裝了 Internet Explorer(Microsoft 的瀏覽器),這使其能夠在市場(chǎng)上占據(jù)一席之地并不斷發(fā)展壯大,最終在瀏覽器領(lǐng)域取得了勝利,這便是著名的「第一次瀏覽器大戰(zhàn)」。
市場(chǎng)份額的快速下滑導(dǎo)致 Netscape 被出售給了 AOL,2003年7月,網(wǎng)景解散,就在解散的當(dāng)天,Mozilla基金會(huì)成立,2004年基于Mozilla源碼的Firefox首次登臺(tái),拉開(kāi)了第二次瀏覽器大戰(zhàn)的序幕。2008 年Netscape最終滅絕,「當(dāng)年的瀏覽器帝國(guó)正式退出了歷史的舞臺(tái)」。
到 2003 年,微軟的 Internet Explorer 控制了 92% 以上的市場(chǎng),完全扭轉(zhuǎn)了 1995 年的局面。然而,雖然微軟在不到十年的時(shí)間里成功地完全接管了瀏覽器市場(chǎng),但很快就會(huì)出現(xiàn)其他競(jìng)爭(zhēng),再次重塑網(wǎng)絡(luò)瀏覽器的歷史。
微軟在 1990 年代后期崛起并讓 Netscape 等公司屈服之后,瀏覽器的歷史似乎已經(jīng)走到了盡頭。然而,正如最初發(fā)布后的情況一樣,Internet Explorer 正在成為劣質(zhì)產(chǎn)品。谷歌于 2008 年推出了其專有瀏覽器——Chrome。到 2012 年底,即推出僅四年后,谷歌 Chrome 瀏覽器憑借其易用性、跨平臺(tái)功能、速度以及與標(biāo)簽和書(shū)簽相關(guān)的特殊功能,取代 Internet Explorer 成為最受歡迎的瀏覽器。
在 2000 年代初期,可能是在微軟將瀏覽器附加到其操作系統(tǒng)之后,Apple 發(fā)布了 Safari,一種專為 Mac 設(shè)計(jì)的瀏覽器,并成為目前市場(chǎng)上第二大瀏覽器。
Internet Explorer 的流行度在 2000 年代后期逐漸減少,主要是因?yàn)樗兊镁徛瓦^(guò)時(shí),而 Microsoft 發(fā)現(xiàn)自己現(xiàn)在似乎已經(jīng)是在外面觀察瀏覽器世界。該公司不想繼續(xù)錯(cuò)過(guò),于是著手解決這個(gè)問(wèn)題,但發(fā)現(xiàn)一個(gè)關(guān)鍵問(wèn)題是“Internet Explorer”這個(gè)名字已經(jīng)成為劣質(zhì)瀏覽器的同義詞。因此,為了嘗試重新進(jìn)入游戲,微軟不得不重新命名,是以,Edge 變誕生了。Edge是微軟瀏覽器的最新版本,它受到了很多好評(píng),但對(duì)于 Microsoft 來(lái)說(shuō),Edge 的出現(xiàn)可能為時(shí)已晚。
「IE瀏覽器終成時(shí)代之淚,Microsoft Edge 成為Windows 11的默認(rèn)瀏覽器」。這是Windows系統(tǒng)更新20年來(lái),IE的首次缺席,也是最后一次。早在Win10更新時(shí)微軟就表示,將放棄更新IE轉(zhuǎn)向開(kāi)發(fā)新的瀏覽器Microsoft Edge。如今是徹底要和桌面上的IE說(shuō)再見(jiàn)了。—— IE 瀏覽器將從 Windows 11 中消失,它也將在 2022 年安息。
瀏覽器市場(chǎng)份額
截止2021年7月初,瀏覽器市場(chǎng)份額如下所示。
瀏覽器使用趨勢(shì)變化:
Web_Browser_Usage_Trends瀏覽器市場(chǎng)份額:
Web_Browser_Market_Share國(guó)內(nèi)瀏覽器市場(chǎng)份額:
Web_Browser_Market_Share_CHN如果你對(duì)以上瀏覽器市場(chǎng)份額數(shù)據(jù)有興趣,可以通過(guò)以下鏈接進(jìn)行查看:
國(guó)內(nèi)瀏覽器市場(chǎng)份額
瀏覽器市場(chǎng)份額[1]
全球?yàn)g覽器市場(chǎng)份額
全球?yàn)g覽器市場(chǎng)份額[2]
w3counter[3]
瀏覽器架構(gòu)
計(jì)算機(jī)的核心
三層計(jì)算機(jī)體系結(jié)構(gòu):底部是機(jī)器硬件,中間是操作系統(tǒng),頂部是應(yīng)用程序。
hw-os-app當(dāng)你在電腦或手機(jī)上啟動(dòng)應(yīng)用時(shí),是?「CPU 和 GPU 為應(yīng)用供能」。通常情況下應(yīng)用是通過(guò)操作系統(tǒng)提供的機(jī)制在 CPU 和 GPU 上運(yùn)行。
CPU
中央處理器(Central Processing Unit),或簡(jiǎn)稱為 CPU。CPU 可以看作是計(jì)算機(jī)的大腦。「一個(gè) CPU 核心如圖中的辦公人員,可以逐一解決很多不同任務(wù)」。它可以在解決從數(shù)學(xué)到藝術(shù)一切任務(wù)的同時(shí)還知道如何響應(yīng)客戶要求。過(guò)去 CPU 大多是單芯片的。隨著現(xiàn)代硬件發(fā)展,你經(jīng)常會(huì)有不止一個(gè)內(nèi)核,為你的手機(jī)和筆記本電腦提供更多的計(jì)算能力。
4 個(gè) CPU 核心作為辦公人員,坐在辦公桌前處理各自的工作:
CPUGPU
圖形處理器(Graphics Processing Unit,簡(jiǎn)稱為 GPU)是計(jì)算機(jī)的另一部件。與 CPU 不同,GPU 擅長(zhǎng)同時(shí)處理跨內(nèi)核的簡(jiǎn)單任務(wù)。顧名思義,「它最初是為解決圖形而開(kāi)發(fā)的」。這就是為什么在圖形環(huán)境中“使用 GPU” 或 “GPU 支持”都與快速渲染和順滑交互有關(guān)。近年來(lái)隨著 GPU 加速計(jì)算的普及,僅靠 GPU 一己之力也使得越來(lái)越多的計(jì)算成為可能。
下圖中,許多帶特定扳手的 GPU 內(nèi)核意味著它們只能處理有限任務(wù)。
GPU進(jìn)程與線程
進(jìn)程可以被描述為是一個(gè)應(yīng)用的執(zhí)行程序。線程是位于進(jìn)程內(nèi)部并執(zhí)行其進(jìn)程程序的任意部分。
啟動(dòng)應(yīng)用時(shí)會(huì)創(chuàng)建一個(gè)進(jìn)程。程序也許會(huì)創(chuàng)建一個(gè)或多個(gè)線程來(lái)幫助它工作。操作系統(tǒng)為進(jìn)程提供了一個(gè)可以使用的“一塊”內(nèi)存,所有應(yīng)用程序狀態(tài)都保存在該私有內(nèi)存空間中。關(guān)閉應(yīng)用程序時(shí),相應(yīng)的進(jìn)程也會(huì)消失,操作系統(tǒng)會(huì)釋放內(nèi)存(下圖中,邊界框?yàn)檫M(jìn)程,線程作為抽象魚(yú)在進(jìn)程中游動(dòng))。
memory進(jìn)程可以請(qǐng)求操作系統(tǒng)啟動(dòng)另一個(gè)進(jìn)程來(lái)執(zhí)行不同的任務(wù)。此時(shí),內(nèi)存中的不同部分會(huì)分給新進(jìn)程。如果兩個(gè)進(jìn)程需要對(duì)話,他們可以通過(guò)**進(jìn)程間通信(IPC)**來(lái)進(jìn)行。許多應(yīng)用都是這樣設(shè)計(jì)的,所以如果一個(gè)工作進(jìn)程失去響應(yīng),該進(jìn)程就可以在不停止應(yīng)用程序不同部分的其他進(jìn)程運(yùn)行的情況下重新啟動(dòng)。
workerprocess.gif瀏覽器的進(jìn)程/線程架構(gòu)模型
瀏覽器進(jìn)程分類
關(guān)于如何「構(gòu)建 web 瀏覽器并不存在標(biāo)準(zhǔn)規(guī)范」,一個(gè)瀏覽器的構(gòu)建方法可能與另一個(gè)迥然不同。不同瀏覽器的進(jìn)程/線程架構(gòu)一般由下圖幾部分:
browser-archChrome多進(jìn)程架構(gòu)
而當(dāng)下“瀏覽器世界的王者” Chrome 架構(gòu)如下圖所示,渲染進(jìn)程下顯示了多個(gè)層,表明 Chrome 為每個(gè)標(biāo)簽頁(yè)運(yùn)行多個(gè)渲染進(jìn)程。
browser-arch-chrome上圖中,頂部是瀏覽器進(jìn)程,它與處理應(yīng)用其它模塊任務(wù)的進(jìn)程進(jìn)行協(xié)調(diào)。對(duì)于渲染進(jìn)程來(lái)說(shuō),創(chuàng)建了多個(gè)渲染進(jìn)程并分配給了每個(gè)標(biāo)簽頁(yè)。Chrome 在可能的情況下會(huì)給每個(gè)標(biāo)簽頁(yè)分配一個(gè)進(jìn)程。而現(xiàn)在它試圖給每個(gè)站點(diǎn)分配一個(gè)進(jìn)程,包括 iframe。
瀏覽器進(jìn)程:控制應(yīng)用中的 “Chrome” 部分,包括地址欄,書(shū)簽,回退與前進(jìn)按鈕,以及處理 web 瀏覽器中網(wǎng)絡(luò)請(qǐng)求、文件訪問(wèn)等不可見(jiàn)的特權(quán)部分;
渲染進(jìn)程:控制標(biāo)簽頁(yè)內(nèi)網(wǎng)站展示;
插件進(jìn)程:控制站點(diǎn)使用的任意插件,如 Flash;
GPU進(jìn)程:處理獨(dú)立于其它進(jìn)程的 GPU 任務(wù)。GPU 被分成不同進(jìn)程,因?yàn)?GPU 處理來(lái)自多個(gè)不同應(yīng)用的請(qǐng)求并繪制在相同表面。
可以簡(jiǎn)單理解為不同進(jìn)程對(duì)應(yīng)瀏覽器 UI 的不同部分:
browserui「Chrome 更多的是把自己抽象為一個(gè)操作系統(tǒng),網(wǎng)頁(yè)或擴(kuò)展相當(dāng)于一個(gè)個(gè)程序」,你甚至可以發(fā)現(xiàn),Chrome 確實(shí)自帶了一個(gè)任務(wù)管理器,在任務(wù)管理器面板會(huì)列出當(dāng)前正在運(yùn)行的進(jìn)程以及它們當(dāng)前的 CPU/內(nèi)存使用量情況等信息。
一般你可以通過(guò)兩種方法打開(kāi)Chrome任務(wù)管理器:
通過(guò)在瀏覽器頂欄(標(biāo)簽tab欄)右側(cè)右鍵,選擇任務(wù)管理器查看;
點(diǎn)擊 Chrome 瀏覽器右上角的“選項(xiàng)”菜單(一般是三個(gè)點(diǎn)的標(biāo)識(shí)),選擇“更多工具”子菜單,點(diǎn)擊“任務(wù)管理器”,打開(kāi)任務(wù)管理器窗口。
前文中提到了 Chrome 使用多個(gè)渲染進(jìn)程,那他有什么優(yōu)勢(shì)呢?
穩(wěn)定性:最簡(jiǎn)單的情況下,你可以想象每個(gè)標(biāo)簽頁(yè)都有自己的渲染進(jìn)程。假設(shè)你打開(kāi)了三個(gè)標(biāo)簽頁(yè),每個(gè)標(biāo)簽頁(yè)都擁有自己獨(dú)立的渲染進(jìn)程。如果某個(gè)標(biāo)簽頁(yè)失去響應(yīng),你可以關(guān)掉這個(gè)標(biāo)簽頁(yè),此時(shí)其它標(biāo)簽頁(yè)依然運(yùn)行著,可以正常使用。如果所有標(biāo)簽頁(yè)都運(yùn)行在同一進(jìn)程上,那么當(dāng)某個(gè)失去響應(yīng),所有標(biāo)簽頁(yè)都會(huì)失去響應(yīng),顯然這樣的體驗(yàn)會(huì)很糟糕。下面是多/單進(jìn)程架構(gòu)的對(duì)比動(dòng)圖,供你參考。
安全性與沙箱化:把瀏覽器工作分成多個(gè)進(jìn)程的另一好處是安全性與沙箱化。由于操作系統(tǒng)提供了限制進(jìn)程權(quán)限的方法,瀏覽器就可以用沙箱保護(hù)某些特定功能的進(jìn)程。例如,Chrome 瀏覽器可以限制處理用戶輸入(如渲染器)的進(jìn)程的文件訪問(wèn)的權(quán)限。
由于進(jìn)程有自己的私有內(nèi)存空間,所以它們通常包含公共基礎(chǔ)設(shè)施的拷貝(如Chrome V8引擎)。這意味著使用了更多的內(nèi)存,如果它們是同一進(jìn)程中的線程,就無(wú)法共享這些拷貝(同一個(gè)進(jìn)程中的線程不共享堆棧,堆棧是保證線程獨(dú)立運(yùn)行所必須的)。為了節(jié)省內(nèi)存,Chrome 對(duì)可啟動(dòng)的進(jìn)程數(shù)量有所限制。具體限制數(shù)值依設(shè)備可提供的內(nèi)存與 CPU 能力而定,但是「當(dāng) Chrome 運(yùn)行時(shí)達(dá)到限制時(shí),會(huì)開(kāi)始在同一站點(diǎn)的不同標(biāo)簽頁(yè)上運(yùn)行同一進(jìn)程」。
Chrome 正在經(jīng)歷架構(gòu)變革,它轉(zhuǎn)變?yōu)閷g覽器程序的每一模塊作為一個(gè)服務(wù)來(lái)運(yùn)行,從而可以輕松實(shí)現(xiàn)進(jìn)程的拆解或聚合。具體表現(xiàn)是,當(dāng) Chrome 運(yùn)行在「強(qiáng)力硬件」上時(shí),它會(huì)將每個(gè)服務(wù)分解到不同進(jìn)程中,從而「提升穩(wěn)定性」,但是如果 Chrome 運(yùn)行在資源有限的設(shè)備上時(shí),它會(huì)將服務(wù)聚合到一個(gè)進(jìn)程中從而「節(jié)省了內(nèi)存占用」。在這一架構(gòu)變革實(shí)現(xiàn)前,類似的整合進(jìn)程以減少內(nèi)存使用的方法已經(jīng)在 Android 類平臺(tái)上使用。
servicfication.gifChrome 67 版本后,桌面版 Chrome 都默認(rèn)開(kāi)啟了「站點(diǎn)隔離」,每個(gè)標(biāo)簽頁(yè)的 iframe 都有一個(gè)單獨(dú)的渲染進(jìn)程。啟用站點(diǎn)隔離是多年來(lái)工程人員努力的結(jié)果。站點(diǎn)隔離并不只是分配不同的渲染進(jìn)程這么簡(jiǎn)單。它從根本上改變了 iframe 的通信方式。在一個(gè)頁(yè)面上打開(kāi)開(kāi)發(fā)者工具,讓 iframe 在不同的進(jìn)程上運(yùn)行,這意味著開(kāi)發(fā)者工具必須在幕后工作,以使它看起來(lái)無(wú)縫。即使運(yùn)行一個(gè)簡(jiǎn)單的 Ctrl + F 來(lái)查找頁(yè)面中的一個(gè)單詞,也意味著在不同的渲染器進(jìn)程中進(jìn)行搜索。你可以看到為什么「瀏覽器工程師把發(fā)布站點(diǎn)隔離功能作為一個(gè)重要里程碑」!
isolation延伸閱讀:Chrome 為什么多進(jìn)程而不是多線程?[4]
瀏覽器整體架構(gòu)
如果您是一名前端工程師,那么,面試時(shí)你大概率會(huì)被問(wèn)到過(guò):從 URL 輸入到頁(yè)面展現(xiàn)到底發(fā)生了什么?,如果您對(duì)這一過(guò)程不太熟悉,建議看看下面兩篇文章,在此不過(guò)多贅述:
經(jīng)典面試題:從 URL 輸入到頁(yè)面展現(xiàn)到底發(fā)生什么?[5]
在瀏覽器輸入 URL 回車(chē)之后發(fā)生了什么(超詳細(xì)版)[6]
瀏覽器的主要任務(wù)之一就是渲染展示頁(yè)面,不同的瀏覽器內(nèi)核,渲染過(guò)程也不完全相同,但大致流程都差不多,下面這張圖片是火狐瀏覽器(Firefox,可以認(rèn)為是Netscapede的涅槃重生)開(kāi)發(fā)文檔中的一張圖片。
瀏覽器架構(gòu)上面這張圖片大體揭示了瀏覽器的渲染展示流程,但是從瀏覽器的整體架構(gòu)上來(lái)說(shuō),上面的圖片展示的也許只是瀏覽器體系中的冰山一角。
通常意義下,瀏覽器架構(gòu)是如下圖這樣的:
瀏覽器架構(gòu)用戶界面
包括地址欄、前進(jìn)/后退按鈕、書(shū)簽菜單等。除了瀏覽器主窗口顯示的您請(qǐng)求的頁(yè)面外,其他顯示的各個(gè)部分都屬于用戶界面。
瀏覽器引擎
用戶界面和渲染引擎的橋梁,在用戶界面和渲染引擎之間傳送指令。瀏覽器引擎提供了開(kāi)始加載URL資源 和一些其他高級(jí)操作方法,比如:重新加載、前進(jìn)、后退動(dòng)作,錯(cuò)誤信息、加載進(jìn)度等。
渲染引擎
負(fù)責(zé)顯示請(qǐng)求的內(nèi)容。如果請(qǐng)求的內(nèi)容是 HTML,它就負(fù)責(zé)解析 HTML 和 CSS 內(nèi)容,并將解析后的內(nèi)容顯示在屏幕上。
所謂瀏覽器內(nèi)核就是指瀏覽器最重要或者說(shuō)核心的部分"Rendering Engine",譯為"渲染引擎"。負(fù)責(zé)對(duì)網(wǎng)頁(yè)語(yǔ)法的解析,比如HTML、JavaScript,并渲染到網(wǎng)頁(yè)上。所以瀏覽器內(nèi)核也就是瀏覽器所采用的渲染引擎,渲染引擎決定這瀏覽器如何顯示頁(yè)面的內(nèi)容和頁(yè)面的格式信息。不同的瀏覽器內(nèi)核對(duì)語(yǔ)法的解釋也不相同,因此同一網(wǎng)頁(yè)在不同內(nèi)核的瀏覽器顯示的效果也會(huì)有差異(瀏覽器兼容)。這也就是網(wǎng)頁(yè)開(kāi)發(fā)者在不需要同內(nèi)核的瀏覽器中測(cè)試網(wǎng)頁(yè)顯示效果的原因。
RENDERING ENGINE延伸閱讀:曾紅極一時(shí)的紅芯瀏覽器,官網(wǎng)對(duì)其介紹是:擁有智能的認(rèn)證引擎、渲染引擎、管控引擎,而且還有強(qiáng)大的“國(guó)密通訊協(xié)議”,支持統(tǒng)一管控、遠(yuǎn)程控制。2018年8月15日,紅芯瀏覽器被爆出打開(kāi)安裝目錄后出現(xiàn)大量和谷歌Chrome瀏覽器一致的同名文件,其安裝程序的文件屬性中也顯示了原始文件名chrome.exe,紅芯瀏覽器的官網(wǎng)已撤下了瀏覽器的下載鏈接。8月16日,紅芯聯(lián)合創(chuàng)始人高婧回應(yīng),紅芯瀏覽器“包含‘Chrome’在里面”,但并非抄襲,而是“站在巨人的肩膀上去做創(chuàng)新”。
hongxin言歸正傳,瀏覽器內(nèi)核主要包括以下三個(gè)技術(shù)分支:排版渲染引擎、 JavaScript引擎,以及其他。
排版引擎:
KHTML:KHTML,是HTML網(wǎng)頁(yè)排版引擎之一,由KDE所開(kāi)發(fā)。KHTML擁有速度快捷的優(yōu)點(diǎn),但對(duì)錯(cuò)誤語(yǔ)法的容忍度則比Mozilla產(chǎn)品所使用的Gecko引擎小。蘋(píng)果電腦于2002年采納了KHTML,作為開(kāi)發(fā)Safari瀏覽器之用,并發(fā)布所修改的最新及過(guò)去版本源代碼。「后來(lái)發(fā)表的開(kāi)源WebCore及WebKit引擎,它們均是KHTML的衍生產(chǎn)品」。
WebCore:WebCore是「蘋(píng)果公司」開(kāi)發(fā)的排版引擎,它是在另外一個(gè)排版引擎“KHTML”的基礎(chǔ)上而來(lái)的。使用WebCore的主要有Safari瀏覽器。
瀏覽器的內(nèi)核引擎,基本上是四分天下:
Trident: IE 以Trident 作為內(nèi)核引擎;
Gecko: Firefox 是基于 Gecko 開(kāi)發(fā);
WebKit: 誕生于1998年,并于2005年由Apple公司開(kāi)源,Safari, Google Chrome,傲游3,獵豹瀏覽器,百度瀏覽器 opera瀏覽器 基于 Webkit 開(kāi)發(fā)。
Presto: Opera的內(nèi)核,但由于市場(chǎng)選擇問(wèn)題,主要應(yīng)用在手機(jī)平臺(tái)--Opera mini。(2013年2月Opera宣布轉(zhuǎn)向WebKit引擎,2013年4月Opera宣布放棄WEBKIT,跟隨GOOGLE的新開(kāi)發(fā)的blink引擎。)
需要略作補(bǔ)充的是,我們經(jīng)常還會(huì)聽(tīng)到Chromium、Webkit2、Blink這些引擎。
Chromium:基于webkit,08年開(kāi)始作為Chrome的引擎,Chromium瀏覽器是Chrome的實(shí)驗(yàn)版,實(shí)驗(yàn)新特性。可以簡(jiǎn)單地理解為:Chromium為實(shí)驗(yàn)版,具有眾多新特性;Chrome為穩(wěn)定版。
圖片來(lái)源:萬(wàn)字詳文:深入理解瀏覽器原理[7]
Webkit2:2010年隨OS X Lion一起面世。WebCore層面實(shí)現(xiàn)進(jìn)程隔離與Google的沙箱設(shè)計(jì)存在沖突。
Blink:基于Webkit2分支,是WebKit中WebCore組件的一個(gè)分支,13年谷歌開(kāi)始作為Chrome 28的引擎集成在Chromium瀏覽器里。Android的WebView同樣基于Webkit2,是現(xiàn)在對(duì)新特性支持度最好的內(nèi)核。Opera(15及往后版本)和Yandex瀏覽器中也在使用。
移動(dòng)端基本上全部是 Webkit 或 Blink 內(nèi)核(除去 Android 上騰訊家的 X5),這兩個(gè)內(nèi)核對(duì)新特性的支持度較高,所以新特性可以在移動(dòng)端大展身手。
各內(nèi)核關(guān)系圖:
KHTML下面我們以WebKit為列,進(jìn)行簡(jiǎn)單介紹,以便讓你對(duì)渲染引擎有一個(gè)更多的理解。WebKit由多個(gè)重要模塊組成,通過(guò)下圖我們可以對(duì)WebKit有個(gè)整體的了解:
WebKitWebKit就是一個(gè)「頁(yè)面渲染以及邏輯處理引擎」,前端工程師把HTML、JavaScript、CSS這“三駕馬車(chē)”作為輸入,經(jīng)過(guò)WebKit的處理,就輸出成了我們能看到以及操作的Web頁(yè)面。從上圖我們可以看出來(lái),WebKit由圖中框住的四個(gè)部分組成。而其中最主要的就是WebCore和JSCore(或者是其它JS引擎)。除此之外,WebKit Embedding API是負(fù)責(zé)瀏覽器UI與WebKit進(jìn)行交互的部分,而WebKit Ports則是讓W(xué)ebkit更加方便的移植到各個(gè)操作系統(tǒng)、平臺(tái)上,提供的一些調(diào)用Native Library的接口,比如在渲染層面,在iOS系統(tǒng)中,Safari是交給CoreGraphics處理,而在Android系統(tǒng)中,Webkit則是交給Skia。
WebKit的渲染流程:
WebKit-Rendering首先瀏覽器通過(guò)URL定位到了一堆由HTML、CSS、JS組成的資源文件,通過(guò)加載器把資源文件給WebCore。之后HTML Parser會(huì)把HTML解析成DOM樹(shù),CSS Parser會(huì)把CSS解析成CSSOM樹(shù)。最后把這兩棵樹(shù)合并,生成最終需要的渲染樹(shù),再經(jīng)過(guò)布局,與具體WebKit Ports的渲染接口,把渲染樹(shù)渲染輸出到屏幕上,成為了最終呈現(xiàn)在用戶面前的Web頁(yè)面。
網(wǎng)絡(luò)
用于網(wǎng)絡(luò)調(diào)用,比如 HTTP 請(qǐng)求。其接口與平臺(tái)無(wú)關(guān),并為所有平臺(tái)提供底層實(shí)現(xiàn),負(fù)責(zé)網(wǎng)絡(luò)通信和安全。
JavaScript 解釋器
用于解析和執(zhí)行 JavaScript 代碼,執(zhí)行結(jié)果將傳遞給渲染引擎來(lái)展示。
用戶界面后端
用于繪制基本的窗口小部件,比如組合框和窗口。其公開(kāi)了與平臺(tái)無(wú)關(guān)的通用接口,而在底層使用操作系統(tǒng)的用戶界面方法。
數(shù)據(jù)存儲(chǔ)
這是持久層,瀏覽器需要在硬盤(pán)上保存各種數(shù)據(jù),例如 Cookie。新的 HTML 規(guī)范 (HTML5) 定義了“網(wǎng)絡(luò)數(shù)據(jù)庫(kù)”,這是一個(gè)完整而輕便的瀏覽器內(nèi)數(shù)據(jù)庫(kù)。
求同存異的瀏覽器架構(gòu)
下面列出了部分瀏覽器的架構(gòu)圖,也許有些架構(gòu)已經(jīng)改變,有興趣可以簡(jiǎn)單參考看看,除了IE之外,大體上各瀏覽器的整體架構(gòu)都是類似的。
Mosaic架構(gòu):
Architecture_of_MosaicFirefox架構(gòu):
Architecture_of_MozillaChrome架構(gòu):
Architecture_of_ChromeSafari架構(gòu):
Architecture_of_SafariIE架構(gòu):
IE架構(gòu)瀏覽器基本原理
Chrome V8
V8一詞最早見(jiàn)于“V-8 engine”,即V8發(fā)動(dòng)機(jī),一般使用在中高端車(chē)輛上。8個(gè)氣缸分成兩組,每組4個(gè),成V型排列。是高層次汽車(chē)運(yùn)動(dòng)中最常見(jiàn)的發(fā)動(dòng)機(jī)結(jié)構(gòu),尤其在美國(guó),IRL,ChampCar和NASCAR都要求使用V8發(fā)動(dòng)機(jī)。
關(guān)于Chrome V8,筆者曾有一篇筆記做了比較詳細(xì)的介紹,全文脈絡(luò)如下,感興趣可以參考閱讀[8]。
Chrome-V8V8是依托Chrome發(fā)展起來(lái)的,后面確不局限于瀏覽器內(nèi)核。發(fā)展至今V8應(yīng)用于很多場(chǎng)景,例如流行的nodejs,weex,快應(yīng)用,早期的RN。V8曾經(jīng)歷過(guò)一次比較大的架構(gòu)調(diào)整,主要變化在于“從字節(jié)碼的放棄到真香”。
V8 的早期架構(gòu)
V8引擎誕生的使命就是要在速度和內(nèi)存回收上進(jìn)行革命。JavaScriptCore的架構(gòu)是采用生成字節(jié)碼的方式,然后執(zhí)行字節(jié)碼。Google覺(jué)得JavaScriptCore這套架構(gòu)不行,生成字節(jié)碼會(huì)浪費(fèi)時(shí)間,不如直接生成機(jī)器碼快。所以V8在前期的架構(gòu)設(shè)計(jì)上是非常激進(jìn)的,采用了直接編譯成機(jī)器碼的方式。后期的實(shí)踐證明Google的這套架構(gòu)速度是有改善,但是同時(shí)也造成了「內(nèi)存消耗問(wèn)題」。
V8-2010早期的V8有Full-Codegen和Crankshaft兩個(gè)編譯器。V8 首先用 Full-Codegen把所有的代碼都編譯一次,生成對(duì)應(yīng)的機(jī)器碼。JS在執(zhí)行的過(guò)程中,V8內(nèi)置的Profiler篩選出熱點(diǎn)函數(shù)并且記錄參數(shù)的反饋類型,然后交給 Crankshaft 來(lái)進(jìn)行優(yōu)化。所以Full-Codegen本質(zhì)上是生成的是未優(yōu)化的機(jī)器碼,而Crankshaft生成的是優(yōu)化過(guò)的機(jī)器碼。
隨著網(wǎng)頁(yè)的復(fù)雜化,V8也漸漸的暴露出了自己架構(gòu)上的缺陷:
Full-Codegen 編譯直接生成機(jī)器碼,導(dǎo)致「內(nèi)存占用大」;
Full-Codegen 編譯直接生成機(jī)器碼,導(dǎo)致「編譯時(shí)間長(zhǎng)」,導(dǎo)致「啟動(dòng)速度慢」;
Crankshaft 無(wú)法優(yōu)化try,catch和finally等關(guān)鍵字劃分的代碼塊;
Crankshaft 新加語(yǔ)法支持,需要為此編寫(xiě)適配不同的Cpu架構(gòu)代碼。
V8 的現(xiàn)有架構(gòu)
為了解決上述缺點(diǎn),V8借鑒JavaScriptCore的架構(gòu),生成字節(jié)碼。V8采用生成字節(jié)碼的方式后,整體流程如下圖:
V8-2017現(xiàn)在的 V8 是一個(gè)非常復(fù)雜的項(xiàng)目,有超過(guò) 100 萬(wàn)行 C++代碼。它由許多子模塊構(gòu)成,其中這 4 個(gè)模塊是最重要的:
Parser[9]:負(fù)責(zé)將 JavaScript 源碼轉(zhuǎn)換為 Abstract Syntax Tree (AST)
確切的說(shuō),在“Parser”將 JavaScript 源碼轉(zhuǎn)換為 AST前,還有一個(gè)叫”Scanner“的過(guò)程,具體流程如下:
ScannerIgnition[10]:interpreter,即解釋器,負(fù)責(zé)將 AST 轉(zhuǎn)換為 Bytecode,解釋執(zhí)行 Bytecode;同時(shí)收集 TurboFan 優(yōu)化編譯所需的信息,比如函數(shù)參數(shù)的類型;解釋器執(zhí)行時(shí)主要有四個(gè)模塊,內(nèi)存中的字節(jié)碼、寄存器、棧、堆。Ignition的原始動(dòng)機(jī)是減少移動(dòng)設(shè)備上的內(nèi)存消耗。在Ignition之前,V8的Full-codegen基線編譯器生成的代碼通常占據(jù)Chrome整體JavaScript堆的近三分之一。這為Web應(yīng)用程序的實(shí)際數(shù)據(jù)留下了更少的空間。Ignition的字節(jié)碼可以直接用TurboFan生成優(yōu)化的機(jī)器代碼,而不必像Crankshaft那樣從源代碼重新編譯。Ignition的字節(jié)碼在V8中提供了更清晰且更不容易出錯(cuò)的基線執(zhí)行模型,簡(jiǎn)化了去優(yōu)化機(jī)制,這是V8 自適應(yīng)優(yōu)化的關(guān)鍵特性。最后,由于生成字節(jié)碼比生成Full-codegen的基線編譯代碼更快,因此激活I(lǐng)gnition通常會(huì)改善腳本啟動(dòng)時(shí)間,從而改善網(wǎng)頁(yè)加載。
TurboFan[11]:compiler,即優(yōu)化編譯器,利用 Ignition 所收集的類型信息,將 Bytecode 轉(zhuǎn)換為優(yōu)化的匯編代碼;TurboFan項(xiàng)目最初于2013年底啟動(dòng),旨在解決Crankshaft的缺點(diǎn)。Crankshaft只能優(yōu)化JavaScript語(yǔ)言的子集。例如,它不是設(shè)計(jì)用于使用結(jié)構(gòu)化異常處理優(yōu)化JavaScript代碼,即由JavaScript的try,catch和finally關(guān)鍵字劃分的代碼塊。很難在Crankshaft中添加對(duì)新語(yǔ)言功能的支持,因?yàn)檫@些功能幾乎總是需要為九個(gè)支持的平臺(tái)編寫(xiě)特定于體系結(jié)構(gòu)的代碼。
Orinoco[12]:garbage collector,垃圾回收模塊,負(fù)責(zé)將程序不再需要的內(nèi)存空間回收。
采用新的Ignition+TurboFan架構(gòu)后,比Full-codegen+Crankshaft架構(gòu)內(nèi)存降低一半多,且70%左右的網(wǎng)頁(yè)速度得到了提升。
在運(yùn)行 C、C++以及 Java 等程序之前,需要進(jìn)行編譯,不能直接執(zhí)行源碼;但對(duì)于 JavaScript 來(lái)說(shuō),我們可以直接執(zhí)行源碼(比如:node test.js),它是在運(yùn)行的時(shí)候先編譯再執(zhí)行,這種方式被稱為「即時(shí)編譯(Just-in-time compilation)」,簡(jiǎn)稱為 JIT。因此,V8 也屬于?「JIT 編譯器」。
JavaScriptCore
V8未誕生之前,早期主流的JavaScript引擎是JavaScriptCore引擎。JavaScriptCore(以下簡(jiǎn)稱JSCore)主要服務(wù)于Webkit瀏覽器內(nèi)核,他們都是由蘋(píng)果公司開(kāi)發(fā)并開(kāi)源出來(lái)。JSCore是WebKit默認(rèn)內(nèi)嵌的JS引擎,之所以說(shuō)是默認(rèn)內(nèi)嵌,是因?yàn)楹芏嗷赪ebKit分支開(kāi)發(fā)的瀏覽器引擎都開(kāi)發(fā)了自家的JS引擎,其中最出名的就是前文提到的Chrome的V8。這些「JS引擎的使命都是解釋執(zhí)行JS腳本」。而在渲染流程上,JS和DOM樹(shù)之間存在著互相關(guān)聯(lián),這是因?yàn)闉g覽器中的JS腳本最主要的功能就是操作DOM樹(shù),并與之交互。我們可以通過(guò)下圖看下它的工作流程:
JavaScriptCoreJavaScriptCore主要模塊:「Lexer 詞法分析器,將腳本源碼分解成一系列的Token;Parser 語(yǔ)法分析器,處理Token并生成相應(yīng)的語(yǔ)法樹(shù);LLInt 低級(jí)解釋器,執(zhí)行Parser生成的二進(jìn)制代碼;Baseline JIT 基線JIT(just in time 實(shí)時(shí)編譯);DFG 低延遲優(yōu)化的JIT;FTL 高通量?jī)?yōu)化的JIT」。
可以看到,相比靜態(tài)編譯語(yǔ)言生成語(yǔ)法樹(shù)之后,還需要進(jìn)行鏈接,裝載生成可執(zhí)行文件等操作,解釋型語(yǔ)言在流程上要簡(jiǎn)化很多。這張流程圖右邊畫(huà)框的部分就是JSCore的組成部分:Lexer(詞法分析)、Parser(語(yǔ)法分析)、LLInt以及JIT(解釋執(zhí)行)的部分(之所以JIT的部分是用橙色標(biāo)注,是因?yàn)椴⒉皇撬械腏SCore中都有JIT部分)。
「詞法分析」很好理解,就是「把一段我們寫(xiě)的源代碼分解成Token序列的過(guò)程」,這一過(guò)程也叫「分詞」。在JSCore,詞法分析是由Lexer來(lái)完成(有的編譯器或者解釋器把分詞叫做Scanner,比如Chrome v8)。
跟人類語(yǔ)言一樣,我們講話的時(shí)候其實(shí)是按照約定俗成,交流習(xí)慣按照一定的語(yǔ)法講出一個(gè)又一個(gè)詞語(yǔ)。那類比到計(jì)算機(jī)語(yǔ)言,計(jì)算機(jī)要理解一門(mén)計(jì)算機(jī)語(yǔ)言,也要理解一個(gè)語(yǔ)句的語(yǔ)法。「Parser會(huì)把Lexer分析之后生成的token序列進(jìn)行語(yǔ)法分析,并生成對(duì)應(yīng)的一棵抽象語(yǔ)法樹(shù)(AST)」。之后,ByteCodeGenerator會(huì)根據(jù)AST來(lái)生成JSCore的字節(jié)碼,完成整個(gè)「語(yǔ)法解析」步驟。
JS源代碼經(jīng)過(guò)了詞法分析和語(yǔ)法分析這兩個(gè)步驟,轉(zhuǎn)成了字節(jié)碼,其實(shí)就是經(jīng)過(guò)任何一門(mén)程序語(yǔ)言必經(jīng)的步驟–編譯。但是不同于我們編譯運(yùn)行OC代碼,JS編譯結(jié)束之后,并不會(huì)生成存放在內(nèi)存或者硬盤(pán)之中的目標(biāo)代碼或可執(zhí)行文件。生成的指令字節(jié)碼,會(huì)被立即被JSCore這臺(tái)虛擬機(jī)進(jìn)行逐行「解釋執(zhí)行」。運(yùn)行指令字節(jié)碼(ByteCode)是JS引擎中很核心的部分,各家JS引擎的優(yōu)化也主要集中于此。
PS:嚴(yán)格的講,語(yǔ)言本身并不存在編譯型或者是解釋型,因?yàn)檎Z(yǔ)言只是一些抽象的定義與約束,并不要求具體的實(shí)現(xiàn),執(zhí)行方式。這里講JS是一門(mén)“解釋型語(yǔ)言”只是JS一般是被JS引擎動(dòng)態(tài)解釋執(zhí)行,而并不是語(yǔ)言本身的屬性。
如果對(duì)JavaScriptCore有更多興趣,關(guān)于JavaScriptCore的更多細(xì)節(jié),建議延伸閱讀以下幾篇博文:
深入理解JSCore[13]
深入剖析 JavaScriptCore[14]
JavaScriptCore 全面解析[15]
深入淺出 JavaScriptCore[16]
瀏覽器與JavaScript
這一小結(jié),還是以Chrome V8為例,簡(jiǎn)單闡述瀏覽器與JavaScript的關(guān)系。
在?「V8 出現(xiàn)之前,所有的 JavaScript 虛擬機(jī)所采用的都是解釋執(zhí)行的方式,這是 JavaScript 執(zhí)行速度過(guò)慢的一個(gè)主要原因」。而 V8 率先引入了「即時(shí)編譯(JIT)**的**雙輪驅(qū)動(dòng)」的設(shè)計(jì)(混合使用編譯器和解釋器的技術(shù)),這是一種權(quán)衡策略,「混合編譯執(zhí)行和解釋執(zhí)行這兩種手段」,給 JavaScript 的執(zhí)行速度帶來(lái)了極大的提升。V8 出現(xiàn)之后,各大廠商也都在自己的 JavaScript 虛擬機(jī)中引入了 JIT 機(jī)制,所以目前市面上 JavaScript 虛擬機(jī)都有著類似的架構(gòu)。另外,「V8 也是早于其他虛擬機(jī)引入了惰性編譯、內(nèi)聯(lián)緩存、隱藏類等機(jī)制,進(jìn)一步優(yōu)化了 JavaScript 代碼的編譯執(zhí)行效率」。
V8 執(zhí)行一段 JavaScript 的流程
V8 執(zhí)行一段 JavaScript 的流程如下圖所示:
V8執(zhí)行一段JavaScript流程圖結(jié)合上文介紹的Chrome V8 架構(gòu),聚焦到JavaScript上,瀏覽器拿到JavaScript源碼,Parser,Ignition 以及 TurboFan 可以將 JS 源碼編譯為匯編代碼,其流程圖如下:
V8流程簡(jiǎn)單地說(shuō),Parser 將 JS 源碼轉(zhuǎn)換為 AST,然后 Ignition 將 AST 轉(zhuǎn)換為 Bytecode,最后 TurboFan 將 Bytecode 轉(zhuǎn)換為經(jīng)過(guò)優(yōu)化的 Machine Code(實(shí)際上是匯編代碼)。
如果函數(shù)沒(méi)有被調(diào)用,則 V8 不會(huì)去編譯它。
如果函數(shù)只被調(diào)用 1 次,則 Ignition 將其編譯 Bytecode 就直接解釋執(zhí)行了。TurboFan 不會(huì)進(jìn)行優(yōu)化編譯,因?yàn)樗枰?Ignition 收集函數(shù)執(zhí)行時(shí)的類型信息。這就要求函數(shù)至少需要執(zhí)行 1 次,TurboFan 才有可能進(jìn)行優(yōu)化編譯。
如果函數(shù)被調(diào)用多次,則它有可能會(huì)被識(shí)別為「熱點(diǎn)函數(shù)」,且 Ignition 收集的類型信息證明可以進(jìn)行優(yōu)化編譯的話,這時(shí) TurboFan 則會(huì)將 Bytecode 編譯為 Optimized Machine Code(已優(yōu)化的機(jī)器碼),以提高代碼的執(zhí)行性能。
圖片中的紅色虛線是逆向的,也就是說(shuō) Optimized Machine Code 會(huì)被還原為 Bytecode,這個(gè)過(guò)程叫做?「Deoptimization」。這是因?yàn)?Ignition 收集的信息可能是錯(cuò)誤的,比如 add 函數(shù)的參數(shù)之前是整數(shù),后來(lái)又變成了字符串。生成的 Optimized Machine Code 已經(jīng)假定 add 函數(shù)的參數(shù)是整數(shù),那當(dāng)然是錯(cuò)誤的,于是需要進(jìn)行 Deoptimization。
function?add(x,?y)?{return?x?+?y; }add(1,?2); add('1',?'2');「V8 本質(zhì)上是一個(gè)虛擬機(jī)」,因?yàn)橛?jì)算機(jī)只能識(shí)別二進(jìn)制指令,所以要讓計(jì)算機(jī)執(zhí)行一段高級(jí)語(yǔ)言通常有兩種手段:
第一種是將高級(jí)代碼轉(zhuǎn)換為二進(jìn)制代碼,再讓計(jì)算機(jī)去執(zhí)行;
另外一種方式是在計(jì)算機(jī)安裝一個(gè)解釋器,并由解釋器來(lái)解釋執(zhí)行。
解釋執(zhí)行和編譯執(zhí)行都有各自的優(yōu)缺點(diǎn),「解釋執(zhí)行啟動(dòng)速度快,但是執(zhí)行時(shí)速度慢,而編譯執(zhí)行啟動(dòng)速度慢,但是執(zhí)行速度快」。為了充分地利用解釋執(zhí)行和編譯執(zhí)行的優(yōu)點(diǎn),規(guī)避其缺點(diǎn),「V8 采用了一種權(quán)衡策略,在啟動(dòng)過(guò)程中采用了解釋執(zhí)行的策略,但是如果某段代碼的執(zhí)行頻率超過(guò)一個(gè)值,那么 V8 就會(huì)采用優(yōu)化編譯器將其編譯成執(zhí)行效率更加高效的機(jī)器代碼」。
簡(jiǎn)單總結(jié)如下,「V8 執(zhí)行一段 JavaScript 代碼所經(jīng)歷的主要流程」包括:
初始化基礎(chǔ)環(huán)境;
解析源碼生成 AST 和作用域;
依據(jù) AST 和作用域生成字節(jié)碼;
解釋執(zhí)行字節(jié)碼;
監(jiān)聽(tīng)熱點(diǎn)代碼;
優(yōu)化熱點(diǎn)代碼為二進(jìn)制的機(jī)器代碼;
反優(yōu)化生成的二進(jìn)制機(jī)器代碼。
Chrome V8 的事件機(jī)制
關(guān)于異步編程和消息隊(duì)列,UI 線程提供一個(gè)消息隊(duì)列,并將待執(zhí)行的事件添加到消息隊(duì)列中,然后 UI 線程會(huì)不斷循環(huán)地從消息隊(duì)列中取出事件、執(zhí)行事件,通用 UI 線程宏觀架構(gòu)如下圖所示:
v8-ui瀏覽器的不同形態(tài)
WebView
「WebView 是一種嵌入式瀏覽器,原生應(yīng)用可以用它來(lái)展示網(wǎng)絡(luò)內(nèi)容」。WebView 只是一個(gè)「可視化的」組件/控件/微件等。這樣我們可以用它來(lái)作為我們?cè)?app 的視覺(jué)部分。當(dāng)你使用原生應(yīng)用時(shí),WebView 可能只是被隱藏在普通的原生 UI 元素中,你甚至用不到注意到它。
如果你把瀏覽器想象成兩部分,一部分是 UI(地址欄,導(dǎo)航欄按鈕等),其它部分是把標(biāo)記跟代碼轉(zhuǎn)換成我們可見(jiàn)和可交互視圖的引擎。「WebView 就是瀏覽器引擎部分」,你可以像插入 iframe 一樣將 Webview 插入到你的原生應(yīng)用中,并且編程化的告訴它將會(huì)加載什么網(wǎng)頁(yè)內(nèi)容。
運(yùn)行在你的 WebView 中的 JavaScript 有能力調(diào)用原生的系統(tǒng) API。這意味著你不必受到 Web 代碼通常必須遵守的傳統(tǒng)瀏覽器安全沙箱的限制。下圖解釋了使用這種技術(shù)后的架構(gòu)差異:
webview and webapp默認(rèn)情況下,在 WebView 或 Web 瀏覽器中運(yùn)行的任何 Web 代碼都與應(yīng)用的其余部分保持隔離。這樣做是出于安全原因,主要是為降低惡意的 JavaScript 代碼對(duì)系統(tǒng)造成的傷害。對(duì)于任意 Web 內(nèi)容,這種安全級(jí)別很有意義,因?yàn)槟阌肋h(yuǎn)不能完全信任加載的 Web 內(nèi)容。但 WebView 的情況并非如此,對(duì)于 WebView 方案,開(kāi)發(fā)人員通常可以完全控制加載的內(nèi)容。惡意代碼進(jìn)入并在設(shè)備上造成混亂的可能性非常低。
「這就是為什么對(duì)于 WebView,開(kāi)發(fā)人員可以使用各種受支持的方式來(lái)覆蓋默認(rèn)的安全行為,并讓 Web 代碼和原生應(yīng)用代碼相互通信。這種溝通通常稱為 bridge」。你可以在上文的圖片中看到 bridge 可視化為 Native Bridge 和 JavaScript Bridge 的一部分。
WebView 非常好,雖然它看起來(lái)像是完全特殊和獨(dú)特的,但請(qǐng)記住,它們只不過(guò)是一個(gè)在應(yīng)用中設(shè)置好位置和大小的、沒(méi)有任何花哨 UI 的瀏覽器,這就是它的精髓。大多數(shù)情況下,除非您調(diào)用原生 API,否則您不必在 WebView 中專門(mén)測(cè)試您的 Web 應(yīng)用程序。此外,您在 WebView 中看到的內(nèi)容與您在瀏覽器中看到的內(nèi)容相同,尤其是使用同一渲染引擎時(shí):
在 iOS 上,Web 渲染引擎始終是 WebKit,與 Safari 和 Chrome 相同。是的,你沒(méi)看錯(cuò)。iOS 上的 Chrome 實(shí)際上使用了 WebKit。
在 Android 上的渲染引擎通常是 Blink,與 Chrome 相同。
在 Windows,Linux 和 macOS 上,由于這些是更寬松的桌面平臺(tái),因此在選擇 WebView 風(fēng)格和渲染引擎時(shí)會(huì)有很大的靈活性。你看到的流行渲染引擎將是 Blink(Chrome)和 Trident(Internet Explorer),但是沒(méi)有一個(gè)引擎可以依賴。這完全取決于應(yīng)用以及它正在使用的 WebView 引擎。
WebView 的應(yīng)用:
WebView 最常見(jiàn)的用途之一是顯示鏈接的內(nèi)容;
廣告仍然是原生應(yīng)用最流行的賺錢(qián)方式之一,大多數(shù)廣告是通過(guò) WebView 提供的 Web 內(nèi)容進(jìn)行投放的;
Hybrid Apps,混合應(yīng)用程序很受歡迎有幾個(gè)原因,最大的一個(gè)是提高開(kāi)發(fā)人員的生產(chǎn)力。如果你有一個(gè)可以在瀏覽器中運(yùn)行的響應(yīng)式 Web 應(yīng)用程序,那么讓相同的應(yīng)用程序在各種設(shè)備上與混合應(yīng)用程序一起運(yùn)行是相當(dāng)簡(jiǎn)單的;當(dāng)你對(duì) Web 應(yīng)用進(jìn)行更新時(shí),所有使用它的設(shè)備都可以立即使用該更改,因?yàn)閮?nèi)容來(lái)自一個(gè)集中的服務(wù)器,而如果是純?cè)鷳?yīng)用,部署和更新時(shí),你將不得不經(jīng)歷針對(duì)每個(gè)平臺(tái)的構(gòu)建、審核;
原生應(yīng)用擴(kuò)展,如 Microsoft Office 中類似維基百科這樣的基于網(wǎng)絡(luò)的擴(kuò)展就是通過(guò)一個(gè) WebView 實(shí)現(xiàn)的。
如果你對(duì) WebView 感興趣,可通過(guò)以下幾篇文章繼續(xù)了解:
7.5.1 WebView(網(wǎng)頁(yè)視圖)基本用法[17]
Android:這是一份全面 & 詳細(xì)的Webview使用攻略[18]
Headless browser
「無(wú)頭瀏覽器」是一種未配置圖形用戶界面 (GUI) 的 Web 瀏覽器,通常通過(guò)命令行或網(wǎng)絡(luò)通信來(lái)執(zhí)行。它主要由軟件測(cè)試工程師使用,沒(méi)有 GUI 的瀏覽器執(zhí)行速度更快,因?yàn)樗鼈儾槐乩L制視覺(jué)內(nèi)容。無(wú)頭瀏覽器的最大好處之一是它們能夠在沒(méi)有 GUI 支持的服務(wù)器上運(yùn)行。
Headless 瀏覽器對(duì)于測(cè)試網(wǎng)頁(yè)特別有用,因?yàn)樗鼈兡軌蛳駷g覽器一樣呈現(xiàn)和理解超文本標(biāo)記語(yǔ)言,包括頁(yè)面布局、顏色、字體選擇以及JavaScript和AJAX的執(zhí)行等樣式元素,這些元素在使用其他測(cè)試方法時(shí)通常是不可用的。
Headless_architectureHeadless 瀏覽器有兩個(gè)主要可交付成果:
無(wú)頭庫(kù),它允許嵌入應(yīng)用程序控制瀏覽器并與網(wǎng)頁(yè)交互。
一個(gè)無(wú)頭外殼,它是一個(gè)示例應(yīng)用程序,用于執(zhí)行無(wú)頭 API 的各種功能。
Puppeteer 是一個(gè) Node 庫(kù),他提供了一組用來(lái)操縱 Chrome 的 API, 通俗來(lái)說(shuō)就是一個(gè) headless chrome 瀏覽器 (當(dāng)然你也可以配置成有 UI 的,默認(rèn)是沒(méi)有的)。既然是瀏覽器,那么我們手工可以在瀏覽器上做的事情 Puppeteer 都能勝任, 另外,Puppeteer 翻譯成中文是”木偶”意思,所以聽(tīng)名字就知道,操縱起來(lái)很方便,你可以很方便的操縱她去實(shí)現(xiàn):
1) 生成網(wǎng)頁(yè)截圖或者 PDF 2) 高級(jí)爬蟲(chóng),可以爬取大量異步渲染內(nèi)容的網(wǎng)頁(yè) 3) 實(shí)現(xiàn) UI 自動(dòng)化測(cè)試,模擬鍵盤(pán)輸入、表單自動(dòng)提交、點(diǎn)擊、登錄網(wǎng)頁(yè)等 4) 捕獲站點(diǎn)的時(shí)間線,以便追蹤你的網(wǎng)站,幫助分析網(wǎng)站性能問(wèn)題 5) 模擬不同的設(shè)備 6) ...
Puppeteer 跟 webdriver 以及 PhantomJS 最大的 的不同就是它是站在用戶瀏覽的角度,而 webdriver 和 PhantomJS 最初設(shè)計(jì)就是用來(lái)做自動(dòng)化測(cè)試的,所以它是站在機(jī)器瀏覽的角度來(lái)設(shè)計(jì)的,所以它們 使用的是不同的設(shè)計(jì)哲學(xué)。
Headless Chrome architecture[19]
puppeteer[20]
Puppeteer 入門(mén)教程[21]
結(jié)合項(xiàng)目來(lái)談?wù)?Puppeteer[22]
Electron
Electron(原名為Atom Shell)是 GitHub 開(kāi)發(fā)的一個(gè)開(kāi)源框架。它通過(guò)使用 Node.js(作為后端)和Chromium 的渲染引擎(作為前端)完成跨平臺(tái)的桌面 GUI 應(yīng)用程序的開(kāi)發(fā)。現(xiàn)已被多個(gè)開(kāi)源 Web 應(yīng)用程序用于前端與后端的開(kāi)發(fā),著名項(xiàng)目包括 GitHub 的 Atom 和微軟的 Visual Studio Code。
ElectronElectron Architecture 由多個(gè) Render Process 和一個(gè) Main 進(jìn)程組成。Main Process 啟動(dòng)Render Process,它們之間的通信是通過(guò)IPC [Inter Process Communication],如下圖所示。
Electron_Architecture我們常用的IDE VSCode 就是基于 Electron (原來(lái)叫 Atom Shell) 進(jìn)行開(kāi)發(fā)的。如下圖所示,(點(diǎn)擊 VSCode 幫助【Help】 下的 切換開(kāi)發(fā)人員工具即可打開(kāi)以下面板)。
VSCodeVS Code 的其他的主要組件有:
殼:Monaco Editor[23]
內(nèi)核:Language Server Protocol[24](一個(gè)代碼編輯器)
Debug Adapter Protocol[25]
Xterm.js[26]
延伸閱讀:Electron | Build cross-platform desktop apps with JavaScript, HTML, and CSS[27]
瀏覽器代碼兼容性測(cè)試
caniuse[28]
browseemall[29]
html5test[30]
延伸閱讀
瀏覽器簡(jiǎn)史[31]
Web 瀏覽器相關(guān)的一些概念[32]
瀏覽器的工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘[33]
從瀏覽器多進(jìn)程到 JS 單線程,JS 運(yùn)行機(jī)制最全面的一次梳理[34]
???? 移動(dòng)端 JS 引擎哪家強(qiáng)?美國(guó)硅谷找......
從 V8 角度揭秘你不知道的面試八股文
高性能 JavaScript 引擎 V8 - 垃圾回收
Inside look at modern web browser[35]【一共四篇,可供參考】
參考資料
Inside look at modern web browser[36]
瀏覽器是如何工作的:Chrome V8 讓你更懂 JavaScript[37]
深入理解JSCore[38]
The Story of the Web: A History Of Internet Browsers[39]
PPT - Browser Architecture[40]
JavaScript 引擎 V8 執(zhí)行流程概述[41]
Understanding WebViews[42]
Quantum Up Close: What is a browser engine?[43]
本文首發(fā)于個(gè)人博客[44],歡迎指正和star[45]。
參考資料
[1]
瀏覽器市場(chǎng)份額:https://link.juejin.cn/?target=https%3A%2F%2Ftongji.baidu.com%2Fresearch%2Fsite
[2]全球?yàn)g覽器市場(chǎng)份額:https://link.juejin.cn/?target=https%3A%2F%2Fgs.statcounter.com%2F
[3]w3counter:https://link.juejin.cn/?target=https%3A%2F%2Fwww.w3counter.com%2Fglobalstats.php
[4]Chrome 為什么多進(jìn)程而不是多線程?:https://link.juejin.cn/?target=https%3A%2F%2Fwww.zhihu.com%2Fquestion%2F368712837
[5]經(jīng)典面試題:從 URL 輸入到頁(yè)面展現(xiàn)到底發(fā)生什么?:https://link.juejin.cn/?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F57895541
[6]在瀏覽器輸入 URL 回車(chē)之后發(fā)生了什么(超詳細(xì)版):https://link.juejin.cn/?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F80551769
[7]萬(wàn)字詳文:深入理解瀏覽器原理:https://link.juejin.cn/?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F96986818
[8]參考閱讀:https://link.juejin.cn/?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000037435824
[9]Parser:https://link.juejin.cn/?target=https%3A%2F%2Fv8.dev%2Fblog%2Fscanner
[10]Ignition:https://link.juejin.cn/?target=https%3A%2F%2Fv8.dev%2Fdocs%2Fignition
[11]TurboFan:https://link.juejin.cn/?target=https%3A%2F%2Fv8.dev%2Fdocs%2Fturbofan
[12]Orinoco:https://link.juejin.cn/?target=https%3A%2F%2Fv8.dev%2Fblog%2Ftrash-talk
[13]深入理解JSCore:https://link.juejin.cn/?target=https%3A%2F%2Ftech.meituan.com%2F2018%2F08%2F23%2Fdeep-understanding-of-jscore.html
[14]深入剖析 JavaScriptCore:https://link.juejin.cn/?target=https%3A%2F%2Fming1016.github.io%2F2018%2F04%2F21%2Fdeeply-analyse-javascriptcore%2F
[15]JavaScriptCore 全面解析:https://juejin.cn/post/6844903765582053384
[16]深入淺出 JavaScriptCore:https://link.juejin.cn/?target=https%3A%2F%2Fwww.jianshu.com%2Fp%2Fac534f508fb0
[17]7.5.1 WebView(網(wǎng)頁(yè)視圖)基本用法:https://link.juejin.cn/?target=https%3A%2F%2Fwww.runoob.com%2Fw3cnote%2Fandroid-tutorial-webview.html
[18]Android:這是一份全面 & 詳細(xì)的Webview使用攻略:https://link.juejin.cn/?target=https%3A%2F%2Fwww.jianshu.com%2Fp%2F3c94ae673e2a%2F
[19]Headless Chrome architecture:https://link.juejin.cn/?target=https%3A%2F%2Fwww.cnblogs.com%2Fbigben0123%2Fp%2F13880254.html
[20]puppeteer:https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fpuppeteer%2Fpuppeteer%2F
[21]Puppeteer 入門(mén)教程:https://link.juejin.cn/?target=https%3A%2F%2Fwww.r9it.com%2F20171106%2Fpuppeteer.html
[22]結(jié)合項(xiàng)目來(lái)談?wù)?Puppeteer:https://link.juejin.cn/?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F76237595
[23]Monaco Editor:https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FMicrosoft%2Fmonaco-editor
[24]Language Server Protocol:https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FMicrosoft%2Flanguage-server-protocol
[25]Debug Adapter Protocol:https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FMicrosoft%2Fdebug-adapter-protocol
[26]Xterm.js:https://link.juejin.cn/?target=https%3A%2F%2Fxtermjs.org%2F
[27]Electron | Build cross-platform desktop apps with JavaScript, HTML, and CSS:https://link.juejin.cn/?target=https%3A%2F%2Fdelftswa.gitbooks.io%2Fdesosa2018%2Fcontent%2Felectron%2Fchapter.html
[28]caniuse:https://link.juejin.cn/?target=https%3A%2F%2Fwww.caniuse.com%2F
[29]browseemall:https://link.juejin.cn/?target=https%3A%2F%2Fwww.browseemall.com%2FResources
[30]html5test:https://link.juejin.cn/?target=https%3A%2F%2Fhtml5test.com%2F
[31]瀏覽器簡(jiǎn)史:https://link.juejin.cn/?target=http%3A%2F%2Fwww.cnw.com.cn%2Fzhuanti%2F2009-ie%2F
[32]Web 瀏覽器相關(guān)的一些概念:https://link.juejin.cn/?target=https%3A%2F%2Fkeqingrong.cn%2Fblog%2F2019-11-24-concepts-related-to-web-browsers
[33]瀏覽器的工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘:https://link.juejin.cn/?target=https%3A%2F%2Fwww.html5rocks.com%2Fzh%2Ftutorials%2Finternals%2Fhowbrowserswork%2F
[34]從瀏覽器多進(jìn)程到 JS 單線程,JS 運(yùn)行機(jī)制最全面的一次梳理:https://link.juejin.cn/?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000012925872
[35]Inside look at modern web browser:https://link.juejin.cn/?target=https%3A%2F%2Fdevelopers.google.com%2Fweb%2Fupdates%2F2018%2F09%2Finside-browser-part1
[36]Inside look at modern web browser:https://link.juejin.cn/?target=https%3A%2F%2Fdevelopers.google.com%2Fweb%2Fupdates%2F2018%2F09%2Finside-browser-part1
[37]瀏覽器是如何工作的:Chrome V8 讓你更懂 JavaScript:https://link.juejin.cn/?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000037435824
[38]深入理解JSCore:https://link.juejin.cn/?target=https%3A%2F%2Ftech.meituan.com%2F2018%2F08%2F23%2Fdeep-understanding-of-jscore.html
[39]The Story of the Web: A History Of Internet Browsers:https://link.juejin.cn/?target=https%3A%2F%2Fwww.internetadvisor.com%2Fthe-story-of-the-web-a-history-of-internet-browsers
[40]PPT - Browser Architecture:https://link.juejin.cn/?target=https%3A%2F%2Fsangbui.com%2Fsb-files%2FBrowserArchitecture_ClientSide.pdf
[41]JavaScript 引擎 V8 執(zhí)行流程概述:https://link.juejin.cn/?target=http%3A%2F%2Fblog.itpub.net%2F69912579%2Fviewspace-2668277%2F
[42]Understanding WebViews:https://link.juejin.cn/?target=https%3A%2F%2Fwww.kirupa.com%2Fapps%2Fwebview.htm
[43]Quantum Up Close: What is a browser engine?:https://link.juejin.cn/?target=https%3A%2F%2Fhacks.mozilla.org%2F2017%2F05%2Fquantum-up-close-what-is-a-browser-engine%2F
[44]個(gè)人博客:https://link.juejin.cn/?target=https%3A%2F%2Fking-hcj.github.io%2F2021%2F07%2F11%2Fweb-browser%2F
[45]指正和star:https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fking-hcj%2Fking-hcj.github.io
- END -
看完一鍵三連在看,轉(zhuǎn)發(fā),點(diǎn)贊
是對(duì)文章最大的贊賞,極客重生感謝你
推薦閱讀
開(kāi)源, yyds!
終于有人把云計(jì)算、大數(shù)據(jù)和人工智能講明白了!
硬核致敬Linux !30歲生日快樂(lè)!
總結(jié)
以上是生活随笔為你收集整理的深入理解浏览器原理和架构|硬核的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 攻克数据结构与算法
- 下一篇: Spring Boot返回前端Long型