<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「前端進(jìn)階必備」深入理解現(xiàn)代瀏覽器

          共 10684字,需瀏覽 22分鐘

           ·

          2020-11-12 13:13

          編者按:本文作者李松峰,資深技術(shù)圖書譯者,翻譯出版過(guò)40余部技術(shù)及交互設(shè)計(jì)專著,現(xiàn)任360奇舞團(tuán)Web前端開(kāi)發(fā)資深專家,360前端技術(shù)委員會(huì)委員、W3C AC代表。

          各位,如果你的職業(yè)是開(kāi)挖掘機(jī),你說(shuō)要不要深入理解挖掘機(jī)?通常來(lái)說(shuō),深入理解你操縱的機(jī)器才能最終達(dá)到人機(jī)一體的境界。

          當(dāng)然,你可以說(shuō):不用,因?yàn)槿绻诰驒C(jī)不好使,我可以換一臺(tái)。嗯,也有道理。不過(guò),假如你同時(shí)又是一名前端開(kāi)發(fā)者,那你要不要深入理解瀏覽器呢?注意,身為前端,你不太可能有機(jī)會(huì)因?yàn)闉g覽器不好使就強(qiáng)迫用戶換一個(gè)你認(rèn)為好使的。這時(shí)候,你好像別無(wú)選擇了。

          不過(guò)也不用害怕,今天我們的現(xiàn)代瀏覽器深度游會(huì)非常輕松、快樂(lè)。這首先必須感謝一位名叫Mariko Kosaka(小坂真子,https://kosamari.com/)的同行。她在Scripto工作,2018年9月在Google開(kāi)發(fā)者網(wǎng)站上發(fā)表了“Inside look at modern web browser”系列文章。本文就是她那4篇文章的“集合版”。為什么搞這個(gè)“集合版”?因?yàn)樗?篇文章寫得實(shí)在太好,更難得的是人家親手繪制了一大堆生動(dòng)的配圖和動(dòng)畫,這讓深入理解現(xiàn)代瀏覽器變得更加輕松愉快。

          好了,言歸正傳。本文分4個(gè)部分,對(duì)應(yīng)上述4篇文章(原文鏈接附后)。

          • 架構(gòu):以Chrome為例,介紹現(xiàn)代瀏覽器的實(shí)現(xiàn)架構(gòu)。

          • 導(dǎo)航:從輸入U(xiǎn)RL到獲到HTML響應(yīng)稱為導(dǎo)航。

          • 渲染:瀏覽器解析HTML、下載外部資源、計(jì)算樣式并把網(wǎng)頁(yè)繪制到屏幕上。

          • 交互:用戶輸入事件的處理與優(yōu)化。

          先來(lái)個(gè)小小的序言。很多人在開(kāi)發(fā)網(wǎng)站時(shí),只關(guān)注怎么寫自己的代碼,關(guān)注怎么提升自己的開(kāi)發(fā)效率。這些當(dāng)然重要,但是寫到一定的階段,就應(yīng)該停下來(lái)想想:瀏覽器到底會(huì)怎么運(yùn)行你寫的代碼。如果你能多了解一些瀏覽器,然后對(duì)它好一點(diǎn),那么就會(huì)更容易達(dá)成你提升用戶體驗(yàn)的目標(biāo)。

          架構(gòu)

          Web瀏覽器的架構(gòu),可以實(shí)現(xiàn)為一個(gè)進(jìn)程包含多個(gè)線程,也可以實(shí)現(xiàn)為很多進(jìn)程包含少數(shù)線程通過(guò)IPC通信。如何實(shí)現(xiàn)瀏覽器,并沒(méi)有統(tǒng)一的標(biāo)準(zhǔn)。Chrome最新的架構(gòu):最上層是瀏覽器進(jìn)程,負(fù)責(zé)協(xié)調(diào)承擔(dān)各項(xiàng)工作的其他進(jìn)程,比如實(shí)用程序進(jìn)程、渲染器進(jìn)程、GPU進(jìn)程、插件進(jìn)程等,如下圖所示。

          2a7366175a5ed4731e20074d3bdef4bf.webp

          渲染器進(jìn)程對(duì)應(yīng)新開(kāi)的標(biāo)簽頁(yè),每新開(kāi)一個(gè)標(biāo)簽頁(yè),就會(huì)創(chuàng)建一個(gè)新的渲染器進(jìn)程。不僅如此,Chrome還會(huì)盡量給每個(gè)站點(diǎn)新開(kāi)一個(gè)渲染器進(jìn)程,包括iframe中的站點(diǎn),以實(shí)現(xiàn)站點(diǎn)隔離。

          下面詳細(xì)了解一下每個(gè)進(jìn)程的作用,可以參考下圖。

          • 瀏覽器進(jìn)程:控制瀏覽器這個(gè)應(yīng)用的chrome(主框架)部分,包括地址欄、書簽、前進(jìn)/后退按鈕等,同時(shí)也會(huì)處理瀏覽器不可見(jiàn)的高權(quán)限任務(wù),如發(fā)送網(wǎng)絡(luò)請(qǐng)求、訪問(wèn)文件。

          • 渲染器進(jìn)程:負(fù)責(zé)在標(biāo)簽頁(yè)中顯示網(wǎng)站及處理事件。

          • 插件進(jìn)程:控制網(wǎng)站用到的所有插件。

          • GPU進(jìn)程:在獨(dú)立的進(jìn)程中處理GPU任務(wù)。之所以放到獨(dú)立的進(jìn)程,是因?yàn)镚PU要處理來(lái)自多個(gè)應(yīng)用的請(qǐng)求,但要在同一個(gè)界面上繪制圖形。

          e3c259d3ce5075db1ffadb0bac0c0117.webp

          當(dāng)然,還有其他進(jìn)程,比如擴(kuò)展進(jìn)程、實(shí)用程序進(jìn)程。要知道你的Chrome當(dāng)前打開(kāi)了多少個(gè)進(jìn)程,點(diǎn)擊右上角的按鈕,選擇“更多工具”,再選擇“任務(wù)管理器”。

          Chrome的多進(jìn)程架構(gòu)有哪些優(yōu)點(diǎn)呢?

          最簡(jiǎn)單的情況下,可以想像一個(gè)標(biāo)簽頁(yè)就是一個(gè)渲染器進(jìn)程,比如3個(gè)標(biāo)簽頁(yè)就是3個(gè)渲染器進(jìn)程。這時(shí)候,如果有一個(gè)渲染器崩潰了,只要把它關(guān)掉即可,不會(huì)影響其他標(biāo)簽頁(yè)。如果所有標(biāo)簽頁(yè)都運(yùn)行在一個(gè)進(jìn)程中,那只要有一個(gè)標(biāo)簽頁(yè)卡住,所有標(biāo)簽頁(yè)都會(huì)卡住。

          除此之外,多進(jìn)程架構(gòu)還有助于安全和隔離。因?yàn)椴僮飨到y(tǒng)有限制進(jìn)程特權(quán)的機(jī)制,瀏覽器可以借此限制某些進(jìn)程的能力。比如,Chrome會(huì)限制處理任意用戶輸入的渲染器進(jìn)程,不讓它任意訪問(wèn)文件。

          由于進(jìn)程都有自己私有的內(nèi)存空間,因此每個(gè)進(jìn)程可能都會(huì)保存某個(gè)公共基礎(chǔ)設(shè)施(比如Chrome的JavaScript引擎V8)的多個(gè)副本。這會(huì)導(dǎo)致內(nèi)存占用增多。為節(jié)省內(nèi)存,Chrome會(huì)限制自己可以打開(kāi)的進(jìn)程數(shù)量。限制的條件取決于設(shè)備內(nèi)存和CPU配置。達(dá)到限制條件后,Chrome會(huì)用一個(gè)進(jìn)程處理同一個(gè)站點(diǎn)的多個(gè)標(biāo)簽頁(yè)。

          Chrome架構(gòu)進(jìn)化的目標(biāo)是將整個(gè)瀏覽器程序的不同部分服務(wù)化,便于分割或合并。基本思路是在高配設(shè)備中,每個(gè)服務(wù)獨(dú)立開(kāi)進(jìn)程,保證穩(wěn)定;在低配設(shè)備中,多個(gè)服務(wù)合并為一個(gè)進(jìn)程,節(jié)約資源。同樣的思路也應(yīng)用到了Android上。

          重點(diǎn)說(shuō)一說(shuō)站點(diǎn)隔離(http://t.cn/RgNAwLC)。站點(diǎn)隔離是新近引入Chrome的一個(gè)里程碑式特性,即每個(gè)跨站點(diǎn)iframe都運(yùn)行一個(gè)獨(dú)立的渲染器進(jìn)程。即便像前面說(shuō)的那樣,每個(gè)標(biāo)簽頁(yè)單開(kāi)一個(gè)渲染器進(jìn)程,但允許跨站點(diǎn)的iframe運(yùn)行在同一個(gè)渲染器進(jìn)程中并共享內(nèi)存空間,那安全攻擊仍然有可能繞開(kāi)同源策略(http://t.cn/8s1ySzx),而且有人發(fā)現(xiàn)在現(xiàn)代CPU中,進(jìn)程有可能讀取任意內(nèi)存(http://t.cn/R8FwHoX)。

          進(jìn)程隔離是隔離站點(diǎn)、確保上網(wǎng)安全最有效的方式。Chrome 67桌面版默認(rèn)采用站點(diǎn)隔離。站點(diǎn)隔離是多年工程化努力的結(jié)果,它并非多開(kāi)幾個(gè)渲染器進(jìn)程那么簡(jiǎn)單。比如,不同的iframe運(yùn)行在不同進(jìn)程中,開(kāi)發(fā)工具在后臺(tái)仍然要做到無(wú)縫切換,而且即便簡(jiǎn)單地Ctrl+F查找也會(huì)涉及在不同進(jìn)程中搜索。

          導(dǎo)航

          導(dǎo)航涉及瀏覽器進(jìn)程與線程間為顯示網(wǎng)頁(yè)而通信。一切從用戶在瀏覽器中輸入一個(gè)URL開(kāi)始。輸入U(xiǎn)RL之后,瀏覽器會(huì)通過(guò)互聯(lián)網(wǎng)獲取數(shù)據(jù)并顯示網(wǎng)頁(yè)。從請(qǐng)求網(wǎng)頁(yè)到瀏覽器準(zhǔn)備渲染網(wǎng)頁(yè)的過(guò)程,叫做導(dǎo)航。

          如前所述,標(biāo)簽頁(yè)外面的一切都由瀏覽器進(jìn)程處理。瀏覽器進(jìn)程中有線程(UI線程)負(fù)責(zé)繪制瀏覽器的按鈕和地址欄,有線程(網(wǎng)絡(luò)線程)負(fù)責(zé)處理網(wǎng)絡(luò)請(qǐng)求并從互聯(lián)網(wǎng)接收數(shù)據(jù),有線程(存儲(chǔ)線程)負(fù)責(zé)訪問(wèn)文件和存儲(chǔ)數(shù)據(jù)。

          8e1281bc2de4c4dba1f71cfe163cb608.webp

          下面我們逐步看一看導(dǎo)航的幾個(gè)步驟。

          第一步:處理輸入。UI線程會(huì)判斷用戶輸入的是查詢字符串還是URL。因?yàn)镃hrome的地址欄同時(shí)也是搜索框。

          1602fbebac29b3c3bb52ab4e55cbe942.webp

          第二步:開(kāi)始導(dǎo)航。如果輸入的是URL,UI線程會(huì)通知網(wǎng)絡(luò)線程發(fā)起網(wǎng)絡(luò)調(diào)用,獲取網(wǎng)站內(nèi)容。此時(shí)標(biāo)簽頁(yè)左端顯示旋轉(zhuǎn)圖標(biāo),網(wǎng)絡(luò)線程進(jìn)行DNS查詢、建立TLS連接(對(duì)于HTTPS)。網(wǎng)絡(luò)線程可能收到服務(wù)器的重定向頭部,如HTTP 301。此時(shí)網(wǎng)絡(luò)線程會(huì)跟UI線程溝通,告訴它服務(wù)器要求重定向。然后,再發(fā)起對(duì)另一個(gè)URL的請(qǐng)求。

          e172b1567a5092bd151426c4f8343740.webp

          第三步:讀取響應(yīng)。服務(wù)器返回的響應(yīng)體到來(lái)之后,網(wǎng)絡(luò)線程會(huì)檢查接收到的前幾個(gè)字節(jié)。響應(yīng)的Content-Type頭部應(yīng)該包含數(shù)據(jù)類型,如果沒(méi)有這個(gè)字段,則需要MIME類型嗅探(http://t.cn/Rt2gG2J)。看看Chrome源碼(http://t.cn/Ai9cZI7D)中的注釋就知道這一塊有多難搞。

          d101443e749e5afd7244b3cb819fe022.webp

          如果響應(yīng)是HTML文件,那下一步就是把數(shù)據(jù)交給渲染器進(jìn)程。但如果是一個(gè)zip文件或其他文件,那就意味著是一個(gè)下載請(qǐng)求,需要把數(shù)據(jù)傳給下載管理器。

          此時(shí)也是“安全瀏覽”(https://safebrowsing.google.com/)檢查的環(huán)節(jié)。如果域名和響應(yīng)數(shù)據(jù)匹配已知的惡意網(wǎng)站,網(wǎng)絡(luò)線程會(huì)顯示警告頁(yè)。此外,CORB(Cross Origin Read Blocking,https://www.chromium.org/Home/chromium-security/corb-for-developers)檢查也會(huì)執(zhí)行,以確保敏感的跨站點(diǎn)數(shù)據(jù)不會(huì)發(fā)送給渲染器進(jìn)程。

          第四步:聯(lián)系渲染器進(jìn)程。所有查檢完畢,網(wǎng)絡(luò)線程確認(rèn)瀏覽器可以導(dǎo)航到用戶請(qǐng)求的網(wǎng)站,于是會(huì)通知UI線程數(shù)據(jù)已經(jīng)準(zhǔn)備好了。UI線程會(huì)聯(lián)系渲染器進(jìn)程渲染網(wǎng)頁(yè)。

          c1ac8ed943e98d9d618dd77961deca88.webp

          由于網(wǎng)絡(luò)請(qǐng)求可能要花幾百毫秒才能拿到響應(yīng),這里還會(huì)應(yīng)用一個(gè)優(yōu)化策略。第二步UI線程要求網(wǎng)絡(luò)線程發(fā)送請(qǐng)求后,已經(jīng)知道可能要導(dǎo)航到哪個(gè)網(wǎng)站去了。因此在發(fā)送網(wǎng)絡(luò)請(qǐng)求的同時(shí),UI線程會(huì)提前聯(lián)系或并行啟動(dòng)一個(gè)渲染器進(jìn)程。這樣在網(wǎng)絡(luò)線程收到數(shù)據(jù)后,就已經(jīng)有渲染器進(jìn)程原地待命了。如果發(fā)生了重定向,這個(gè)待命進(jìn)程可能用不上,而是換作其他進(jìn)程去處理。

          第五步:提交導(dǎo)航。數(shù)據(jù)和渲染器進(jìn)程都有了,就可以通過(guò)IPC從瀏覽器進(jìn)程向渲染器進(jìn)程提交導(dǎo)航。渲染器進(jìn)程也會(huì)同時(shí)接收到不間斷的HTML數(shù)據(jù)流。當(dāng)瀏覽器進(jìn)程收到渲染器進(jìn)程的確認(rèn)消息后,導(dǎo)航完成,文檔加載階段開(kāi)始。

          10dbe428a651bce9ce8bf1b36b130223.webp

          此時(shí),地址欄會(huì)更新,安全指示圖標(biāo)和網(wǎng)站設(shè)置UI也會(huì)反映新頁(yè)面的信息。當(dāng)前標(biāo)簽頁(yè)面的會(huì)話歷史會(huì)更新,后退/前進(jìn)按鈕起作用。為便于標(biāo)簽頁(yè)/會(huì)話在關(guān)閉標(biāo)簽頁(yè)或窗口后恢復(fù),會(huì)話歷史會(huì)寫入磁盤。

          最后一步:初始加載完成。提交導(dǎo)航之后,渲染器進(jìn)程將負(fù)責(zé)加載資源和渲染頁(yè)面(具體細(xì)節(jié)后面介紹)。而在“完成”渲染后(在所有iframe中的onload事件觸發(fā)且執(zhí)行完成后),渲染器進(jìn)程會(huì)通過(guò)IPC給瀏覽器進(jìn)程發(fā)送一個(gè)消息。此時(shí),UI線程停止標(biāo)簽頁(yè)上的旋轉(zhuǎn)圖標(biāo)。

          初始加載完成后,客戶端JavaScript仍然可能加載額外資源并重新渲染頁(yè)面。

          如果此時(shí)用戶在地址又輸入了其他URL呢?瀏覽器進(jìn)程還會(huì)重復(fù)上述步驟,導(dǎo)航到新站點(diǎn)。不過(guò)在此之前,需要確認(rèn)已渲染的網(wǎng)站是否關(guān)注beforeunload事件。因?yàn)闃?biāo)簽頁(yè)中的一切,包括JavaScript代碼都由渲染器進(jìn)程處理,所以瀏覽器進(jìn)程必須與當(dāng)前的渲染器進(jìn)程確認(rèn)后再導(dǎo)航到新站點(diǎn)。

          013c66721a99b02afdf8332930f00e26.webp

          如果導(dǎo)航請(qǐng)求來(lái)自當(dāng)前渲染器進(jìn)程(用戶點(diǎn)擊了鏈接或JavaScript運(yùn)行了window.location = "https://newsite.com"),渲染器進(jìn)程首先會(huì)檢查beforeunload處理程序。然后,它會(huì)走一遍與瀏覽器進(jìn)程觸發(fā)導(dǎo)航同樣的過(guò)程。唯一的區(qū)別在于導(dǎo)航請(qǐng)求是由渲染器進(jìn)程提交給瀏覽器進(jìn)程的。

          導(dǎo)航到不同的網(wǎng)站時(shí),會(huì)有一個(gè)新的獨(dú)立渲染器進(jìn)程負(fù)責(zé)處理新導(dǎo)航,而老的渲染器進(jìn)程要負(fù)責(zé)處理unload之類的事件。更多細(xì)節(jié),可以參考“頁(yè)面生命周期API”:http://t.cn/Rey7RIE。

          43802191a32bb043eb31080501c70bd3.webp

          另外,導(dǎo)航階段還可能涉及Service Worker,即網(wǎng)頁(yè)應(yīng)用中的網(wǎng)絡(luò)代理服務(wù)(http://t.cn/R3SH3HL),開(kāi)發(fā)者可以通過(guò)它控制什么緩存在本地,何時(shí)從網(wǎng)絡(luò)獲取新數(shù)據(jù)。Service Worker說(shuō)到底也是需要渲染器進(jìn)程運(yùn)行的JavaScript代碼。如果網(wǎng)站注冊(cè)了Server Worker,那么導(dǎo)航請(qǐng)求到來(lái)時(shí),網(wǎng)絡(luò)線程會(huì)根據(jù)URL將其匹配出來(lái),此時(shí)UI線程就會(huì)聯(lián)系一個(gè)渲染器進(jìn)程來(lái)執(zhí)行Service Worker的代碼:可能只要從本地緩存讀取數(shù)據(jù),也可能需要發(fā)送網(wǎng)絡(luò)請(qǐng)求。

          6d1917a0981821ea55f2e091fa03df80.webp

          如果Service Worker最終決定從網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù),瀏覽器進(jìn)程與渲染器進(jìn)程間的這種往返通信會(huì)導(dǎo)致延遲。因此,這里會(huì)有一個(gè)“導(dǎo)航預(yù)加載”的優(yōu)化(http://t.cn/Ai9qGJ66),即在Service Worker啟動(dòng)同時(shí)預(yù)先加載資源,加載請(qǐng)求通過(guò)HTTP頭部與服務(wù)器溝通,服務(wù)器決定是否完全更新內(nèi)容。

          95a4ef132e68cb1fcd9dd43bf8249584.webp

          渲染

          渲染是渲染器進(jìn)程內(nèi)部的工作,涉及Web性能的諸多方面(詳細(xì)內(nèi)容可以參考這里http://t.cn/Ai9c4nUu)。標(biāo)簽頁(yè)中的一切都由渲染器進(jìn)程負(fù)責(zé)處理,其中主線程負(fù)責(zé)運(yùn)行大多數(shù)客戶端JavaScript代碼,少量代碼可能會(huì)由工作線程處理(如果用到了Web Worker或Service Worker)。合成器(compositor)線程和柵格化(raster)線程負(fù)責(zé)高效、平滑地渲染頁(yè)面。

          0f0e82d955534a499aafc23df51327d2.webp

          渲染器進(jìn)程的核心任務(wù)是把HTML、CSS和JavaScript轉(zhuǎn)換成用戶可以交互的網(wǎng)頁(yè)接下來(lái),我們從整體上過(guò)一遍渲染器進(jìn)程處理Web內(nèi)容的各個(gè)階段。

          解析HTML

          構(gòu)建DOM。渲染器進(jìn)程收到導(dǎo)航的提交消息后,開(kāi)始接收HTML,其主線程開(kāi)始解析文本字符串(HTML),并將它轉(zhuǎn)換為DOM(Document Object Model,文檔對(duì)象模型)。

          DOM是瀏覽器內(nèi)部對(duì)頁(yè)面的表示,也是JavaScript與之交互的數(shù)據(jù)結(jié)構(gòu)和API。

          如何將HTML解析為DOM由HTML標(biāo)準(zhǔn)(http://t.cn/R2NREUt)定義。HTML標(biāo)準(zhǔn)要求瀏覽器兼容錯(cuò)誤的HTML寫法,因此瀏覽器會(huì)“忍氣吞聲”,絕不報(bào)錯(cuò)。詳情可以看看“解析器錯(cuò)誤處理及怪異情形簡(jiǎn)介”(http://t.cn/Ai9c8i5D)。

          加載子資源。網(wǎng)站都會(huì)用到圖片、CSS和JavaScript等外部資源。瀏覽器需要從緩存或網(wǎng)絡(luò)加載這些文件。主線程可以在解析并構(gòu)建DOM的過(guò)程中發(fā)現(xiàn)一個(gè)加載一個(gè),但這樣效率太低。為此,Chrome會(huì)在解析同時(shí)并發(fā)運(yùn)行“預(yù)加載掃描器”,當(dāng)發(fā)現(xiàn)HTML文檔中有時(shí),預(yù)加載掃描器會(huì)將請(qǐng)求提交給瀏覽器進(jìn)程中的網(wǎng)絡(luò)線程。

          bbe64d2126167dcda7b36634082d51fb.webp

          JavaScript可能阻塞解析。如果HTML解析器碰到

          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    狼人香蕉28视频在线 | 性爱视频免费网站 | 欧美一级特黄真人做受 | www乱伦 | 久久叫床 |