<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>

          圖解瀏覽器

          共 5534字,需瀏覽 12分鐘

           ·

          2021-02-26 13:23


          讀完需要5分鐘,速讀僅需3分鐘

          這是前端食堂的第 56 篇原創(chuàng)

          美味值:??????????

          口味:仔梅燒小排

          本文同步視頻版

          01 瀏覽器架構(gòu)演進(jìn)

          開篇我們先來簡單回顧下歷史,從 1993 年發(fā)布的第一款“好用”的瀏覽器 Mosaic,到 1994 年網(wǎng)景公司推出的紅極一時的 Navigator 瀏覽器,圖形用戶界面化的瀏覽器終于開始推動了 Web 技術(shù)的普及和發(fā)展。

          微軟也隨后推出了 IE,加入戰(zhàn)場并取得瀏覽器大戰(zhàn)“一戰(zhàn)”的勝利。戰(zhàn)敗的網(wǎng)景公司索性將 Navigator 源代碼開源,創(chuàng)建了 Mozilla 基金會,并于 2004 年發(fā)布了 Firefox 瀏覽器。

          蘋果公司于 2003 年發(fā)布了 Safari 瀏覽器,Google 公司于 2008 年發(fā)布了 Chrome 瀏覽器。Chrome 瀏覽器在瀏覽器大戰(zhàn)的“二戰(zhàn)”中技壓群雄,拔得頭籌?,F(xiàn)如今也是前端工程師最喜愛的瀏覽器,沒有之一。

          Chrome 瀏覽器從 2007 年以前的單進(jìn)程架構(gòu)到現(xiàn)在的多進(jìn)程架構(gòu),瀏覽器的架構(gòu)在不斷的升級,變得更加穩(wěn)定、更加流暢、更加安全。目前 Chrome 的瀏覽器包括如下進(jìn)程:

          • 1 個瀏覽器(Browser)主進(jìn)程
          • 1 個 GPU 進(jìn)程
          • 1 個網(wǎng)絡(luò)(NetWork)進(jìn)程
          • 多個渲染進(jìn)程(運(yùn)行在沙箱模式下)
          • 多個插件進(jìn)程

          不過,軟件工程可沒有銀彈。瀏覽器的架構(gòu)體系也隨著調(diào)整變得更加復(fù)雜,也會有更高的資源占用。

          那么如何尋求一種在資源占用復(fù)雜架構(gòu)體系之間的平衡便成為了一個難題。

          小孩子才做選擇,魚和熊掌我都要!

          Chrome 團(tuán)隊在 2016 年使用“面向服務(wù)的架構(gòu)”(Services Oriented Architecture,簡稱 SOA)的思想設(shè)計了新的 Chrome 架構(gòu)。

          他們將模塊重構(gòu)成獨(dú)立的服務(wù)(Service),服務(wù)運(yùn)行在獨(dú)立的進(jìn)程中,想要訪問的話必須使用定義好的接口,通過 IPC 來進(jìn)行通信。這樣的架構(gòu)無疑更加內(nèi)聚、松耦合、易于維護(hù)和擴(kuò)展。

          02 瀏覽器導(dǎo)航渲染流程

          從輸入 URL 到頁面展示,這中間發(fā)生了什么?

          這是一道十分常見的面試題,不過大多數(shù)人回答這個問題時都不夠系統(tǒng)和全面,可見這道題能夠充分考察應(yīng)試者的知識深度。

          我畫了一張圖整理了瀏覽器的導(dǎo)航渲染流程,下面我們來一起查缺補(bǔ)漏。

          導(dǎo)航流程

          1. 用戶在地址欄輸入內(nèi)容后,地址欄會將輸入的內(nèi)容進(jìn)行合成 URL。
          2. 當(dāng)用戶輸入完內(nèi)容并按下回車鍵時,瀏覽器會在當(dāng)前頁面執(zhí)行 beforeunload 事件,你可以在這個鉤子中詢問是否要離開當(dāng)前頁面,常見于一些表單提交的場景。
          3. 接下來開始導(dǎo)航流程,瀏覽器進(jìn)入加載狀態(tài)。
          4. 瀏覽器的網(wǎng)絡(luò)進(jìn)程會先查找緩存中是否存在該資源,有的話直接返回,如果沒有的話會發(fā)起 URL 請求。
          5. 接下來首先要進(jìn)行的是 DNS 解析,獲得請求域名的服務(wù)器的 IP 地址(這個過程我也畫了一張圖,放在下文),如果協(xié)議是 HTTPS,還需要建立 TLS 連接。
          6. 接著利用目標(biāo)服務(wù)器的 IP 地址建立 TCP 連接(三次握手),構(gòu)建 HTTP 請求報文,發(fā)起請求。服務(wù)器收到請求后,會根據(jù)請求信息生成響應(yīng)報文。
          7. 瀏覽器的網(wǎng)絡(luò)進(jìn)程接收到響應(yīng)報文后進(jìn)行解析,如果狀態(tài)碼是 301 或者 302,則需要取得響應(yīng)頭中的 Location 對應(yīng)的地址進(jìn)行重定向,再重新發(fā)起請求。
          8. 如果狀態(tài)碼是 200,瀏覽器會根據(jù)響應(yīng)頭中的 Content-Type 字段來識別返回的響應(yīng)體數(shù)據(jù)類型,從而進(jìn)行不同的流程。如 text/html 代表 html 格式, application/octet-stream 代表字節(jié)流類型,瀏覽器會按照下載類型來處理。
          9. 如果是 HTML,瀏覽器會遵循 process-per-site-instance 默認(rèn)策略準(zhǔn)備渲染進(jìn)程,準(zhǔn)備好后就提交文檔(將網(wǎng)絡(luò)進(jìn)程接收到的數(shù)據(jù)提交給渲染進(jìn)程)。文檔被提交后,渲染進(jìn)程便開始進(jìn)行頁面解析和子資源的加載。

          (當(dāng)然在第 7 點(diǎn)中還有 300、303 等 3xx 的狀態(tài)碼,具體含義可以參考我的這一篇專欄 那些年與面試官交手過的HTTP問題)

          process-per-site-instance 默認(rèn)策略:每個標(biāo)簽對應(yīng)一個渲染進(jìn)程,如果從一個頁面打開了一個新頁面,新打開的頁面與當(dāng)前頁面還屬于同一個站點(diǎn)的話,那么新頁面會復(fù)用當(dāng)前頁面的渲染進(jìn)程。

          渲染流程

          渲染流程在上圖中一并畫了出來,需要經(jīng)過以下幾個階段:

          1. 構(gòu)建 DOM 樹
          2. 樣式計算
          3. 布局
          4. 分層
          5. 繪制
          6. 分塊
          7. 光柵化
          8. 合成

          因為渲染流程的內(nèi)容比較多,本文先不詳細(xì)展開,后面我們再開一篇專欄進(jìn)行講解。

          DNS

          DNS 的解析是一個遞歸流程,順序如下圖中數(shù)字標(biāo)記所示:

          • 根 DNS 服務(wù)器:返回頂級域 DNS 服務(wù)器的 IP 地址
          • 頂級 DNS 服務(wù)器:返回權(quán)威 DNS 服務(wù)器的 IP 地址
          • 權(quán)威 DNS 服務(wù)器:返回相應(yīng)主機(jī)的 IP 地址

          03 垃圾回收

          棧中的垃圾數(shù)據(jù)

          先來看一段簡單的示例代碼:

          function hello ({
              var name = '前端食堂'
              var food = { name'回鍋肉' } 
              function world ({
                  var description = { slogan'吃好每一頓飯' }
              }
              world()
          }
          hello()

          上面的代碼所對應(yīng)的內(nèi)存堆??臻g如下圖所示:

          棧中的垃圾回收比較簡單,當(dāng)一個函數(shù)執(zhí)行結(jié)束后,JavaScript 引擎會通過向下移動 ESP 來銷毀函數(shù)調(diào)用棧中所保存的執(zhí)行上下文,ESP 就是記錄當(dāng)前執(zhí)行狀態(tài)的指針。

          堆中的垃圾數(shù)據(jù)

          先來看兩個概念,能夠幫助我們更好的理解堆中的垃圾回收操作。

          代際假說

          堆中的垃圾回收策略都是建立在代際假說的基礎(chǔ)之上,代際假說有以下兩個特點(diǎn):

          1. 大部分對象在內(nèi)存中存在的時間很短,簡單來說,就是很多對象一經(jīng)分配內(nèi)存,很快就變得不可訪問。
          2. 不死的對象,會活得更久。

          分代收集

          在 Chrome 瀏覽器引擎 V8 中會把堆分為新生代老生代兩個區(qū)域,如下圖所示:

          顧名思義,生存時間短的對象放在新生區(qū)中,生存時間久的對象放在老生區(qū)中。

          堆中的垃圾回收需要用到垃圾回收器,分為主垃圾回收器副垃圾回收器。

          副垃圾回收器

          負(fù)責(zé)新生區(qū)的垃圾回收,新生區(qū)區(qū)域不大(為了執(zhí)行效率),回收頻繁。

          新生區(qū)中使用了 Scavenge 算法,該算法會把新生區(qū)的空間劃分為兩個區(qū)域,一半是對象區(qū)域,一半是空閑區(qū)域。

          副垃圾回收器的工作流程如下:

          1. 首先對對象區(qū)域中的垃圾進(jìn)行標(biāo)記。
          2. 標(biāo)記完成后,副垃圾回收器會將存活的對象復(fù)制到空閑區(qū)域中,為了避免產(chǎn)生內(nèi)存碎片,還需要進(jìn)行有序的排列,有序排列相當(dāng)于內(nèi)存整理。
          3. 完成復(fù)制后,將對象區(qū)域和空閑區(qū)域進(jìn)行翻轉(zhuǎn),就完成了垃圾回收的操作。

          翻轉(zhuǎn)的這種操作可以讓對象區(qū)和空閑區(qū)無限重復(fù)的使用,不過由于新生區(qū)空間并不大,很容易會被存活的對象塞滿。所以 V8 引擎采用了對象晉升的策略,經(jīng)過兩次垃圾回收后依然還能存活的對象會被晉升到老生區(qū)中。

          主垃圾回收器

          負(fù)責(zé)老生區(qū)中的垃圾回收,老生區(qū)中對象占用空間大,對象存活時間長。

          除了上文說到的新生區(qū)中晉升的對象,一些大的對象也會直接被分配到老生區(qū)。

          主垃圾回收器是使用了標(biāo)記 - 清除(Mark-Sweep)的算法,工作流程如下:

          1. 首先是標(biāo)記階段,從一組根元素開始遞歸遍歷,能到達(dá)的元素就是活動對象,否則就是垃圾。
          2. 然后使用標(biāo)記 - 清除算法進(jìn)行垃圾回收,不過回收后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片。
          3. 于是又產(chǎn)生了另外一種算法標(biāo)記 - 整理(Mark-Compact),整理時可以讓存活的對象都向一端移動,然后直接清除掉端邊界以外的內(nèi)存。

          全停頓

          垃圾回收操作會暫停 JavaScript 的運(yùn)行,回收完畢后才會恢復(fù)執(zhí)行,這種行為就是全停頓。

          為了降低全停頓所帶來的卡頓,V8 引擎采用了增量標(biāo)記(Incremental Marking) 算法進(jìn)行優(yōu)化,將標(biāo)記過程分為一個個小任務(wù),這些小任務(wù)的執(zhí)行時間比較短,可以穿插在其他的 JavaScript 任務(wù)中間執(zhí)行,這樣就不會有明顯的卡頓了。

          當(dāng)然,V8 所采用的優(yōu)化方案不只這一種,而是多種方案綜合使用的,除了增量回收還有并行回收、并發(fā)回收等。

          • 并行回收:垃圾回收器會使用多個輔助線程來并行執(zhí)行垃圾回收
          • 并發(fā)回收:回收線程在執(zhí)行 JavaScript 的過程中,輔助線程在后臺執(zhí)行垃圾回收

          如果你了解 React 的 Concurrent 模式中時間切片的原理,它的實現(xiàn)思想是不是與增量標(biāo)記算法有異曲同工之妙呢。

          04 核心網(wǎng)頁指標(biāo) Core Web Vitals

          Google 大佬推出了 Core Web Vitals:目的是為了更好的簡化場景,幫助網(wǎng)站專注于最重要的指標(biāo)以提升用戶體驗。

          在 2020 年主要關(guān)注三個方面:加載、交互性和視覺穩(wěn)定性,并包括以下指標(biāo):

          衡量所有 Core Web Vitals 最簡單的方法就是使用 web-vitals 庫,使用起來就像調(diào)用單個函數(shù)一樣簡單。

          import {getCLS, getFID, getLCP} from 'web-vitals';

          getCLS(console.log);
          getFID(console.log);
          getLCP(console.log);

          也可以使用 Chrome 插件 Web Vitals Chrome 來幫助我們測量這些指標(biāo)。

          如果想要直接通過 Web API 來獲取這些指標(biāo)的話可以參考下面的獲取方法:

          • 在JavaScript中測量LCP
          • 在JavaScript中測量FID
          • 在JavaScript中測量CLS

          LCP Largest Contentful Paint 最大內(nèi)容繪制

          LCP用于衡量標(biāo)準(zhǔn)報告視口內(nèi)可見的最大圖像或文本塊的渲染時間,為了提供良好的用戶體驗,網(wǎng)站應(yīng)努力在開始加載頁面的前2.5 秒內(nèi)進(jìn)行“最大內(nèi)容繪制”。

          優(yōu)化LCP方案

          FID First Input Delay 首次交互延遲

          FID用于衡量從用戶第一次與頁面進(jìn)行交互到瀏覽器實際上能夠開始處理事件處理程序的時間。為了提供良好的用戶體驗,網(wǎng)站應(yīng)努力使首次輸入延遲小于 100 毫秒。

          下圖中米色方塊代表主線程處于忙碌階段,如果此時用戶進(jìn)行輸入,則它必須等待任務(wù)完成時才能響應(yīng)輸入,等待的時間也就是此頁面上該用戶的 FID 值。

          優(yōu)化FID方案

          CLS Cumulative Layout Shift 累積布局偏移

          CLS用于測量在頁面的整個生命周期中發(fā)生的每一個意外的布局移動,它代表所有單獨(dú)布局轉(zhuǎn)移分?jǐn)?shù)的總和。為了提供良好的用戶體驗,網(wǎng)站應(yīng)努力使CLS分?jǐn)?shù)小于0.1。

          布局偏移分?jǐn)?shù)

          瀏覽器將查看視口大小以及兩個渲染幀之間的視口中不穩(wěn)定元素的移動。

          布局偏移分?jǐn)?shù)是該運(yùn)動的兩個指標(biāo)的乘積:影響分?jǐn)?shù)和距離分?jǐn)?shù)

          layout shift score = impact fraction * distance fraction

          影響分?jǐn)?shù)

          前一幀和當(dāng)前幀的所有不穩(wěn)定元素的可見區(qū)域的并集(占視口總面積的一部分)是當(dāng)前幀的影響分?jǐn)?shù)。

          在上圖中,有一個元素在一幀中占據(jù)了視口的一半。然后,在下一幀中,元素下移視口高度的 25%。紅色的虛線矩形表示兩個幀中元素的可見區(qū)域的并集,在這種情況下,其為總視口的 75%,因此其影響分?jǐn)?shù)為 0.75。

          距離分?jǐn)?shù)

          布局偏移分?jǐn)?shù)方程的另一部分測量不穩(wěn)定元素相對于視口移動的距離。距離分?jǐn)?shù)是任何不穩(wěn)定元素在框架中(水平或垂直)移動的最大距離除以視口的最大尺寸(寬度或高度,以較大者為準(zhǔn))。

          在上圖中,最大視口尺寸是高度,不穩(wěn)定元素已經(jīng)移動了視口高度的 25%,所以距離分?jǐn)?shù)是 0.25。

          所以,布局偏移分?jǐn)?shù):0.75 * 0.25 = 0.1875

          優(yōu)化CLS方案

          好了,本文到這里就結(jié)束了,文中參考的鏈接都整理到了下面,大家可以自行查閱。

          站在巨人的肩膀上

          • 圖解 Google V8 李兵
          • 瀏覽器工作原理與實踐 李兵
          • Core Web Vitals https://web.dev/vitals/
          • web-vitals https://github.com/GoogleChrome/web-vitals/
          • LCP https://web.dev/lcp/
          • FID https://web.dev/fid/
          • CLS https://web.dev/cls/
          • 優(yōu)化FID方案 https://web.dev/optimize-fid/
          • 優(yōu)化LCP方案 https://web.dev/optimize-lcp/
          • 優(yōu)化CLS方案 https://web.dev/optimize-cls/


          多一個點(diǎn)在看

          多一條小魚干

          瀏覽 55
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  欧美激情xxxx | 中文字幕在线视频观看 | 亚洲AV无码国产精品 | 欧美成人免费人免费 | 国产日韩AV一区二区 |