1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 10684字,需瀏覽 22分鐘

         ·

        2020-11-12 13:13

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

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

        當然,你可以說:不用,因為如果挖掘機不好使,我可以換一臺。嗯,也有道理。不過,假如你同時又是一名前端開發(fā)者,那你要不要深入理解瀏覽器呢?注意,身為前端,你不太可能有機會因為瀏覽器不好使就強迫用戶換一個你認為好使的。這時候,你好像別無選擇了。

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

        好了,言歸正傳。本文分4個部分,對應上述4篇文章(原文鏈接附后)。

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

        • 導航:從輸入URL到獲到HTML響應稱為導航。

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

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

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

        架構

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

        2a7366175a5ed4731e20074d3bdef4bf.webp

        渲染器進程對應新開的標簽頁,每新開一個標簽頁,就會創(chuàng)建一個新的渲染器進程。不僅如此,Chrome還會盡量給每個站點新開一個渲染器進程,包括iframe中的站點,以實現(xiàn)站點隔離。

        下面詳細了解一下每個進程的作用,可以參考下圖。

        • 瀏覽器進程:控制瀏覽器這個應用的chrome(主框架)部分,包括地址欄、書簽、前進/后退按鈕等,同時也會處理瀏覽器不可見的高權限任務,如發(fā)送網(wǎng)絡請求、訪問文件。

        • 渲染器進程:負責在標簽頁中顯示網(wǎng)站及處理事件。

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

        • GPU進程:在獨立的進程中處理GPU任務。之所以放到獨立的進程,是因為GPU要處理來自多個應用的請求,但要在同一個界面上繪制圖形。

        e3c259d3ce5075db1ffadb0bac0c0117.webp

        當然,還有其他進程,比如擴展進程、實用程序進程。要知道你的Chrome當前打開了多少個進程,點擊右上角的按鈕,選擇“更多工具”,再選擇“任務管理器”。

        Chrome的多進程架構有哪些優(yōu)點呢?

        最簡單的情況下,可以想像一個標簽頁就是一個渲染器進程,比如3個標簽頁就是3個渲染器進程。這時候,如果有一個渲染器崩潰了,只要把它關掉即可,不會影響其他標簽頁。如果所有標簽頁都運行在一個進程中,那只要有一個標簽頁卡住,所有標簽頁都會卡住。

        除此之外,多進程架構還有助于安全和隔離。因為操作系統(tǒng)有限制進程特權的機制,瀏覽器可以借此限制某些進程的能力。比如,Chrome會限制處理任意用戶輸入的渲染器進程,不讓它任意訪問文件。

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

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

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

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

        導航

        導航涉及瀏覽器進程與線程間為顯示網(wǎng)頁而通信。一切從用戶在瀏覽器中輸入一個URL開始。輸入URL之后,瀏覽器會通過互聯(lián)網(wǎng)獲取數(shù)據(jù)并顯示網(wǎng)頁。從請求網(wǎng)頁到瀏覽器準備渲染網(wǎng)頁的過程,叫做導航。

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

        8e1281bc2de4c4dba1f71cfe163cb608.webp

        下面我們逐步看一看導航的幾個步驟。

        第一步:處理輸入。UI線程會判斷用戶輸入的是查詢字符串還是URL。因為Chrome的地址欄同時也是搜索框。

        1602fbebac29b3c3bb52ab4e55cbe942.webp

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

        e172b1567a5092bd151426c4f8343740.webp

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

        d101443e749e5afd7244b3cb819fe022.webp

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

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

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

        c1ac8ed943e98d9d618dd77961deca88.webp

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

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

        10dbe428a651bce9ce8bf1b36b130223.webp

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

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

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

        如果此時用戶在地址又輸入了其他URL呢?瀏覽器進程還會重復上述步驟,導航到新站點。不過在此之前,需要確認已渲染的網(wǎng)站是否關注beforeunload事件。因為標簽頁中的一切,包括JavaScript代碼都由渲染器進程處理,所以瀏覽器進程必須與當前的渲染器進程確認后再導航到新站點。

        013c66721a99b02afdf8332930f00e26.webp

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

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

        43802191a32bb043eb31080501c70bd3.webp

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

        6d1917a0981821ea55f2e091fa03df80.webp

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

        95a4ef132e68cb1fcd9dd43bf8249584.webp

        渲染

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

        0f0e82d955534a499aafc23df51327d2.webp

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

        解析HTML

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

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

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

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

        bbe64d2126167dcda7b36634082d51fb.webp

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

        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一边揉美女胸一边摸屁股 | 东北少妇高潮抽搐 | 天天操天天干天天 | 免费国产在线麻豆网站 | 考逼视频网站 | 狼友入口 | 一级二级三级毛片 | 成人免费观看视频 | 欧美日韩中文在线视频 | 草逼com |