瀏覽器頁面資源加載過程與優化

評價頁面性能好壞的核心之一就是頁面的加載速度,而頁面加載速度的關鍵就是頁面資源的加載。本文將從瀏覽器瀏覽器頁面資源加載過程展開分析,來引出頁面關鍵請求路徑的概念,並給出如何優化該關鍵請求路徑的一些方法。 下面相關內容,都是以chrome瀏覽器爲例來進行介紹的。不一樣瀏覽器之間,能夠會略有差別,但基本過程是一致的。css

瀏覽器加載資源過程

首先拋出兩個問題:html

  • 瀏覽器如何知道應該加載哪些資源?
  • 瀏覽器是什麼順序來加載這些資源? 當瀏覽器截獲到一個頁面請求後,將會按照順序作以下圖所示的4件事。
    流程
    流程
  1. 首先會將全部須要加載的資源進行分類。
  2. 而後根據瀏覽器相關的安全策略,來決定資源的加載權限。
  3. 接着對各個資源的加載優先級進行計算和排序。
  4. 最後一步,根據加載優先級順序來加載資源。
第一步:資源分類

chrome瀏覽器會將資源分爲14類,以下表所示。前端

類型 介紹
kMainResource 即主資源,html頁面文件資源就屬於該類型
kImage 各類圖片資源
kCSSStyleSheet 顧名思義,就是層疊樣式表css資源
kScript 腳本資源,例如js資源
kFont 字體資源,例如網頁中經常使用的字體集.woff資源
kRaw 混合類型資源,最多見的ajax請求就屬於這類資源
kSVGDocument SVG可縮放矢量圖形文件資源
kXSLStyleSheet 擴展樣式表語言XSLT,是一種轉換語言,關於該類型能夠查閱w3c XSL來了解
kLinkPrefetch HTML5頁面的預讀取資源(Link prefetch),例如dns-prefetch。下面會有詳細介紹
kTextTrack video的字幕資源,- 即<track>標籤
kImportResource HTML Imports,將一個HTML文件導入到其餘HTML文檔中,例如<link href="import/post.html" rel="import" />。詳細瞭解請查閱相關文檔。
kMedia 多媒體資源,video or audio都屬於該類資源
kManifest HTML5 應用程序緩存資源
kMock 預留的測試類型
第二步:安全策略檢查

網頁安全政策(Content Security Policy,縮寫 CSP)是由瀏覽器提供的一種白名單制度。開發者經過配置,來告訴瀏覽器各種外部資源的加載和執行限制,來提升網頁的安全性。一種最經常使用的應用就是經過限制非信任域名腳本的加載來預防XSS攻擊。 能夠經過兩種方式來配置CSP。 第一種,就是經過頁面HTTP請求頭的Content-Security-Policy字段來限制。以下圖所示,這是www.google.com頁面的請求頭: web

流程
流程
第二種是,經過 <meta>標籤來設置。 <meta>是以key-value的方式來進行配置的。下面以幾個具體的應用例子來介紹。

  1. 用於預防XSS:
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; style-src nos.netease.com kaola.com;">
複製代碼

上面的script-src表明腳本資源;style-src表明樣式資源;'self'表明只信任當前域名下的外來資源,其餘域下的資源所有會被攔截;nos.netease.com kaola.com表明信任nos.netease.com和kaola.com這兩個域名下的資源。 因此上面的標籤的意義就是:對於腳本資源只信任本域下的,對於樣式資源,除了本域還會加載nos.netease.com和kaola.com這兩個域名下的。ajax

  1. 用於站點請求協議升級過渡(http轉https):
<meta http-equiv="Content-Secur****ity-Policy" content="upgrade-insecure-requests">
複製代碼

上面的upgrade-insecure-requests的意義,就如同字面意思同樣:升級全部非安全請求。當加了這個meta標籤之後,瀏覽器會將https頁面中的全部htttp請自動升級到https。例如,當咱們須要進行全站http轉https改造時,對於原有的大量http資源會直接強制以https或wss等SSL加密形式發送請求而不會報錯。固然若是資源服務器不支持https等SSL加密,那麼該資源仍是不會載入。chrome

  1. 用於阻止Mixed Content:
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
複製代碼

混合內容(Mixed Content)就是第2個例子所說的,在https站點中,進行的http請求。這類在安全連接中混合了非安全請求內容就叫混合內容。出現這類請求時,咱們能夠在瀏覽器控制檯中找到對應的警告信息,以下圖所示。 json

流程
流程
混合內容會下降HTTPS網站的安全性和用戶體驗。不過讓人略感放心的是,瀏覽器對於可能對安全性形成較大威脅的資源類型的混合模式請求都會直接攔截報錯,例如腳本資源,以下圖所示。但對於圖片、音頻、視頻等資源只會警告,但不會阻止其加載。
流程
流程
對於安全性要求極高的網站,能夠經過上面的標籤來阻止因此類型的非安全連接請求,這樣包括圖片、音頻、視頻等資源也將會被攔截報錯。 固然對於Content-Security-Policy的設置還有不少其餘做用,你們能夠經過 MDN來作進一步瞭解。

第三步:資源優先級計算

資源的優先級被分爲5級。不一樣資料上,對這5級的命名描述上可能有所不一樣。主要是由於資料自己多是從網絡層面瀏覽器內核或者用戶端控制檯顯示這三個方向中的某一個來講的。這三個方向雖然對這5級的命名不一樣,但都是一一對應的。 網絡層面,5級分別爲:Highest、Medium、Low、Lowest、Idle; 瀏覽器內核,5級分別爲:VeryHigh、High、Medium、Low、VeryLow; 用戶端控制檯顯示,5級分別爲:Highest、High、Medium、Low、Lowest;後端

下面是以瀏覽器內核做爲研究方向,來介紹瀏覽器的資源優先級計算過程:瀏覽器

  • 第一步,根據資源的類型來設定默認優先級。 對於每一類資源瀏覽器都有一個默認的加載優先級規則:
  1. html、css、font這三種類型的資源優先級最高;
  2. 而後是preload資源(經過<link rel=「preload">標籤預加載)、script、xhr請求;
  3. 接着是圖片、語音、視頻;
  4. 最低的是prefetch預讀取的資源。
  • 第二步,根據必定的實際規則,對優先級進行調整。 初始優先級設置好之後,瀏覽器會根據資源的實際屬性和位於文檔中的位置等方面,對優先級進行調整,來肯定出最終的加載優先級順序。對於幾個常見資源類型的調整規則以下:
  1. 對於XHR請求資源:將同步XHR請求的優先級調整爲最高。 XHR請求能夠分爲同步請求和異步請求,瀏覽器會把同步請求的優先級提高到最高級,以便儘早獲取數據、加快頁面的顯示。
  2. 對於圖片資源:會根據圖片是否在可見視圖以內來改變優先級。 圖片資源的默認優先級爲Low。現代瀏覽器爲了提升用戶首屏的體驗,在渲染時會計算圖片資源是否在首屏可見視圖以內,在的話,會將這部分視口可見圖片(Image in viewport)資源的優先級提高爲High。
  3. 對於腳本資源:瀏覽器會將根據腳本所處的位置和屬性標籤分爲三類,分別設置優先級。 首先,對於添加了defer/async屬性標籤的腳本的優先級會所有降爲Low。 而後,對於沒有添加該屬性的腳本,根據該腳本在文檔中的位置是在瀏覽器展現的第一張圖片以前仍是以後,又可分爲兩類。在以前的(標記early**)它會被定爲High優先級,在以後的(標記late**)會被設置爲Medium優先級。 下圖總結了資源優先級計算後各種資源的優先級狀況,其中特別將上面講的三種常見資源的狀況框了出來。紅框框中的爲腳本類型、紫框的爲圖片類型、藍框爲XHR請求。圖片來源點此
    流程
    流程
第四步:按照上面計算的安全策略和優先級來加載或阻塞資源。

關鍵請求鏈和優化

上面詳細介紹了瀏覽器的資源加載過程,其中核心在於資源的加載優先順序的計算。咱們能夠經過優化資源的加載優先級順序,來有效提升頁面的加載響應速度。緩存

首先來介紹下關鍵請求鏈(Critical-Request-Chains)的概念。可視區域渲染完畢(首屏),並對於用戶來講可用時,必須加載的資源請求隊列,就叫作關鍵請求鏈。這樣,咱們能夠經過關鍵請求鏈,來肯定優先加載的資源以及加載順序,以實現瀏覽器儘量快地加載頁面。

如何查找頁面的關鍵請求鏈
  1. 經過首屏快照獲取關鍵image資源。 以下圖所示,咱們經過首屏快照,來獲取首屏所要加載的圖片資源。(紅框內)
    流程
    流程
  2. 經過LightHouse插件獲取關鍵請求鏈中的關鍵js和css資源。 LightHouse詳細的使用方法能夠經過點擊此處來了解。經過執行該插件最終能夠生成一個報告,裏面包含了有關該頁面性能的全方面報告和建議。其中有關關鍵請求鏈的報告以下圖所示:
    流程
    流程
  3. 經過瀏覽器控制檯查看各個請求的優先級 打開Chrome控制檯,切換到Network tab下,就能夠查看資源的優先級(Priority)。若是沒有Priority一欄,能夠右鍵在下拉菜單中勾選Priority便可。以下圖所示:
    流程
    流程
優化關鍵請求鏈

優化關鍵請求鏈有不少方法,這裏主要介紹兩種。

  • 第一種:利用PreloadPrefetch

    這兩個標籤在文章前面的介紹中就已經有所介紹,它們都屬於預加載性能優化技術。對於開發人員,咱們可能比瀏覽器更加了解咱們的應用。從而可使用該技術來預先告知瀏覽器某些資源可能在未來會被使用到,讓瀏覽器對這部分資源進行提早加載。 Preload:

    <link rel="preload" href="test.jpg">
    複製代碼

    Prefetch: Prefetch包括資源預加載、DNS預解析、http預鏈接和頁面預渲染。

    資源預加載:<link rel="prefetch" href="test.css">
    DNS預解析:<link rel="dns-prefetch" href="//haitao.nos.netease.com">
    http預鏈接:<link rel="prefetch" href="//www.kaola.com"> 將創建對該域名的TCP連接
    頁面預渲染:<link rel="prerender" href="//m.kaola.com"> 將會預先加載連接文檔的全部資源
    複製代碼

    那麼PrefetchPreload有什麼區別呢? 具體來說,Preload來告訴瀏覽器預先請求當前頁須要的資源,從而提升這些資源的請求優先級。好比,對於那些原本請求優先級較低的關鍵請求,咱們能夠經過設置Prefetch來提高這些請求的優先級。 Prefetch來告訴瀏覽器用戶未來可能在其餘頁面(非本頁面)可能使用到的資源,那麼瀏覽器會在空閒時,就去預先加載這些資源放在http緩存內,最多見的dns-prefetch。好比,當咱們在瀏覽A頁面,若是會經過A頁面中的連接跳轉到B頁面,而B頁面中咱們有些資源但願儘早提早加載,那麼咱們就能夠在A頁面裏添加這些資源Prefetch,那麼當瀏覽器空閒時,就會去加載這些資源。 因此,對於那些可能在當前頁面使用到的資源能夠利用Prefetch,而對一些可能在未來的某些頁面中被使用的資源能夠利用Preload。若是從加載優先級上看,Prefetch會提高請求優先級;而Preload會把資源的優先級放在最低,當瀏覽器空閒時纔去預加載。

    • 潑盆冷水: 既然PrefetchPreload做用如此強大,咱們是否能夠放心使用呢?但實際上,除了dns-prefetch,其餘的兼容性都十分堪憂。特別是在Safari上,因爲蘋果公司對安全性的苛刻要求,基本上對這些預加載技術都未實現支持。 Preload的支持性以下圖所示,發現新版chrome支持較好,但Safari全軍覆沒。
      流程
      流程
      dns-prefetch支持性還不錯。
      流程
      流程
      Prefetch一樣的Safari全軍覆沒。
      流程
      流程
  • 第二種:利用LocalStorage。 既然第一種的預加載技術來進行資源緩存的支持性較差,那麼一般能夠利用LocalStorage來對部分請求的數據和結果進行緩存,省去發送http請求所消耗的時間,從而提升網頁的響應速度。 這類作法在移動端應用已經十分普遍。下面分別介紹鵝、貓、狗三家頁面是如何利用LS來進行請求緩存的。

  • 微信:利用LS來對js文件進行緩存。 以下圖所示,用瀏覽器打開一篇微信公衆帳號文章,打開控制檯,發現Network里居然一個js文件都不須要加載?一臉懵逼!

    流程
    流程
    切到LS才譁然大悟,原來全部的JS都藏在這裏了!
    流程
    流程
    微信就是利用了這種技巧來緩存關鍵路徑裏的js資源,從而大大加快了頁面訪問速度。 固然,實際實現起來,並不像表面看得那樣,第一次訪問時將js放到LS裏,每次進來取出來執行這麼簡單,最核心的實際上是須要設計一套緩存更新機制。首先咱們對於緩存的js文件要經過後綴來設置獨一無二的版本標識;其次,每次後端須要傳一份資源配置文件,前端會根據這個配置文件來和LS中緩存的文件進行版本標識匹配,從而決定是利用LS緩存,仍是從新發請求更新資源。例如,微信中的這個配置文件就是經過moon_map來同步輸出給前端的,以下圖所示:
    流程
    流程

  • 天貓:利用LS來對關鍵的XHR異步請求進行緩存。 以天貓超市首頁爲例: 以下圖,查看LS,發現其對首屏中的輪播和10個分類入口的數據進行了緩存。

    流程
    流程
    上面的json內容,格式化後,發現其中包含banner和flowIcons這兩個屬性,裏面的數據分別對應的就是輪播和分類入口的數據。這樣就能夠大大提高首屏的渲染展現時間。

  • 京東:利用LS來對非關鍵請求進行緩存。 以PC版的京東首頁爲例。京東反向思惟,另闢奇徑地採用了另外一種方式來利用LS。那就是把非關鍵請求剝離出來存放在LS內。 具體來講,對於首屏數據,仍是正常加載和展現。但爲了非首屏數據的加載和渲染會阻塞和搶佔資源,從而影響首屏頁面渲染。因此將非首屏資源的HTML/CSS等資源抽出來放在LS內,當頁面滾動到可視區域時再去LS中獲取數據,插入到dom中。這點很相似於如今的模塊懶加載。以下圖所示,每一個LS裏都包含了一個模塊所須要的HTML/CSS的資源。

    流程
    流程

PS:廣告一波,網易考拉前端招人啦~有興趣的戳我投遞簡歷

END

參考資料:

  1. 從Chrome源碼看瀏覽器如何加載資源:https://zhuanlan.zhihu.com/p/30558018。
  2. Preload,Prefetch 和它們在 Chrome 之中的優先級:https://yq.aliyun.com/articles/226240
  3. 聊聊瀏覽器資源加載優化:http://qingbob.com/let-us-talk-about-resource-load/
  4. 關鍵請求:http://www.zcfy.cc/article/the-critical-request-css-tricks-3843.html
  5. 其餘:相關內容的MDN文檔及Google Web Develop文檔。
相關文章
相關標籤/搜索