前端優化之 Http 相關優化總結


學習和總結文章同步發佈於 https://github.com/xianshanna...,有興趣可以關注一下,一起學習和進步。

Http 優化方式是前端性能優化的重要部分,也是前端必備的知識點之一。

減少靜態資源文件大小

這個是最根本的途徑,假設真的有個 10 幾兆以上的靜態資源文件,不減少大小的情況下,即使優化做到了極致,用戶體驗也好不了哪裏去。

如果整個網頁就 2K 大小的資源文件,不優化都很快。

代碼層優化

  • 只打包用到的依賴包,目前 webpack tree shaking 功能已經自動處理了,還有儘量少使用第三方依賴包(當然看情況啦)。
  • 代碼分割(code splitting),不同頁面加載自己用到的代碼,不加載其他頁面的代碼(其實也屬於懶加載)。

傳輸層優化

Http 傳承啓用壓縮傳輸方式。

一般我們開啓 gzip,基本都能壓縮 6 倍左右(一般都是文件越大,字符串相似率越大,壓縮率越大)。

首先經過服務器壓縮後,然後 Http 響應頭 Content-Encoding 設置爲相應的壓縮方式,瀏覽器會自動解壓的。

Content-Encoding: gzip

當然還有其他的壓縮方式,如 compress、deflate 等等,目前使用最廣的還是 gzip。

適當合併或者分散請求

合併請求或者分散請求需要看實際情況的。

合併請求

http 1.1 (包括 http1.1)之前的版本,瀏覽器存在同域名併發限制,谷歌目前是同域名並發現在爲6 個請求,其他的瀏覽器或多或少,但也差不了多少。

如果是使用的是 http1.1 web 服務,那麼我們首次加載的資源要基本保證在 4 個以內,所以靜態資源請求數過多就要看情況進行合併請求了。

分散請求

Http2.0 沒有同域名併發的問題,我們可以適當分散請求。當然如果在 Http1.1 一個資源文件過大,然後併發並沒有達到限制,也可以拆分資源文件達到分散請求的目的。

使用預加載

預加載某些情況下可以大大提升加載速度進而提示用戶體驗。

預加載需要了解 preloadprefetch 的知識。

預加載 DNS

dns 解析也是需要時間的,特別在移動端的時候更明顯,我們可以預解析 dns 減少不通域名 dns 解析時間。

    <link rel="dns-prefetch" href="//example.com">

其實還有個 preconnectpreconnect 不僅完成 DNS 預解析,同時還將進行 TCP 握手和建立傳輸層協議,但是瀏覽器有兼容性,目前用不上。

<link rel="preconnect" href="http://example.com">

預加載靜態資源

使用 preload

通過 preload 一般是預加載當前頁面要用到的圖片、字體、js 腳本、css 文件等靜態資源文件。

場景一

如果需要,你可以完全以腳本化的方式來執行這些預加載操作。例如,我們在這裏創建一個HTMLLinkElement 實例,然後將他們附加到 DOM 上:

var preloadLink = document.createElement("link");
preloadLink.href = "myscript.js";
preloadLink.rel = "preload";
preloadLink.as = "script";
document.head.appendChild(preloadLink);

這意味着瀏覽器將預加載這個JavaScript文件,但並不實際執行它。

如果要對其加以執行,在需要的時候,你可以執行:

var preloadedScript = document.createElement("script");
preloadedScript.src = "myscript.js";
document.body.appendChild(preloadedScript);

當你需要預加載一個腳本,但需要推遲到需要的時候才令其執行時,這種方式會特別有用。

場景二

字體是要使用到的時候纔會去加載字體的(如果字體是自定義的字體,會發起 Http 請求加載字體)。

由於這個特性,我們可以預加載字體,待使用到字體的時候,字體已經加載完畢,無需等待加載。

如下我們沒有 preload 的時候,代碼也是可以運行的,但是字體加載是需要等待頁面 JS、CSS 資源加載完畢後,當前頁面使用到字體纔會去加載的:

<style>
  @font-face {
    font-family: Test-Number-Medium;
    src: url(./static/font/Test-Number-Medium.otf);
  }
</style>

我們加上:

<link rel="preload" href="./static/font/Test-Number-Medium.otf">

就可以提交加載,節省大部分甚至全部的字體加載時間,一般都是全部的時間,因爲 JS 資源文件比字體大多了(並行下載,最長的資源加載時間,決定了最大加載時間)。

使用 prefech

prefetch 一般是預加載非當前頁面的資源,prefetch 是一個低優先級的資源提示,允許瀏覽器在後臺(空閒時)獲取將來可能用得到的資源,並且將他們存儲在瀏覽器的緩存中。當前頁面加載完畢,纔會開始下載 d帶有 prefetch 標記的資源,然後當用戶進入另外一個頁面,已經 prefetched 的資源可以立刻從緩存中加載。

不過 prefech 的應用場景比較少。

<link rel="prefetch" href="/uploads/images/pic.png">

採用懶加載

圖片懶加載

這種做法一般都是在用戶滾動到響應位置(當然從用戶體驗式來說,需要提前一點加載),纔會加載響應的圖片,圖片特別多的網上基本都會做這個優化(如視頻網站)。

或者幻燈片查看圖片的時候,用戶即將查查下一張圖片的時候再加載,而不是一次性加載全部的圖片。

JS 懶加載

需要用到相關 JS 時,通過動態創建 <script> 標籤進行 JS 文件懶加載,如 Webpack 的 code splitting

合理使用 defer 和 async

帶有 deferasync 屬性的 script 資源都會並行下載,而且不會影響頁面的解析,從而達到了節省腳本下載時間

兩種的不同的在於:

帶有 defer 屬性的資源會按照順序在頁面出現的屬性,資源加載完後,會在 DOMContentLoaded事件調用之前依次執行。

帶有 async 屬性的資源則是下載完立即執行,可能在 DOMContentLoaded 事件之前或者之後執行,多個帶有 async 屬性的資源無執行順序,誰先加載完成,誰先執行。

那麼爲什麼可以節省下載時間?我們來對比一下。

defer 資源加載類似於把 <script> 放在 </body> 前,但是 defer 資源可以和 <head> 的資源並行下載,那麼就可以節省部分甚至全部的下載時間(看 <head> 資源和 defer 資源大小)。所以我們何以適當的把 defer 資源放在 <head>,某些場景是可以提升一定的速度。

async 資源有點類似於 </body> 前的 script 資源加載完成後,動態創建 <script> 標籤加載資源,但是卻要等待頁面 JS 文件執行之後纔可以加載,而 async 資源無需等待即可提前加載,就可以節省一定的加載時間。所以比較適合加載如谷歌分析百度統計日誌上報等類型的 js 資源,都是獨立運作也不影響頁面的輔助 js 資源。

利用緩存

緩存對於再次訪問相同資源來說,是個極大的優化,緩存是 http 優化的必經之道。對於 css 和 js 這些靜態資源文件,我們一般都是用強緩存(例如緩存30天),強緩存無需再次向服務請求靜態資源。
但是強緩存如果使用不當,那麼會對用戶造成意想不到的 bug,如入口 html 文件就不能被強緩存了,否則版本更新後,用戶在緩存期間是無法訪問到新版的頁面。

詳細可以看下本人的另一篇緩存相關的文章,瀏覽器之HTTP緩存的那些事

使用 Http2.0

Http2.0 多路複用解決了多域名並發現在問題,可以節省資源總體的下載時間,還有請求頭壓縮和差異傳輸也會提高傳輸效率。

多路複用

HTTP1.1 持久連接解決了連接複用問題,但還是存在着一個問題,一個 TCP 無法併發處理請求:在一個 TCP 連接中,同一時間只能夠發送一個請求,並且需要等響應完成才能夠發送第二個請求。

HTTP2.0 使用了多路複用的技術,做到同一個連接併發處理多個請求,而且併發請求的數量比 HTTP1.1 大了好幾個數量級。

當然 HTTP1.1 也可以多建立幾個 TCP 連接,來支持處理更多併發的請求,但是創建TCP連接本身也是有開銷的。

TCP 連接有一個預熱和保護的過程,先檢查數據是否傳送成功,一旦成功過,則慢慢加大傳輸速度。因此對應瞬時併發的連接,服務器的響應就會變慢。所以最好能使用一個建立好的連接,並且這個連接可以支持瞬時併發的請求。

多路複用能帶來哪些優化呢?

有多路複用特性,那麼瀏覽器對同一域名的鏈接數的限制也是沒必要的了(HTTP1.1 的谷歌對統一域名併發請求最多支持 6個 持久鏈接)。

那麼我們可以根據實際情況進行資源拆分,從而節省下載時間,無併發請求限制的情況下,下載的時間是根據並行下載的最長時間來算的,無需等待上一個資源下載,才能進行另外一個資源的下載,在資源比較多的情況下,這將大大提升資源總體的下載速度。

以前的 CSS 的雪碧圖 優化手段,在多路複用的特性下,已經是沒必要的了。

多路複用還帶來了,延遲低的優化,這也是速度提升的一方面。

二進制分幀

在應用層與傳輸層之間增加一個二進制分幀層,以此達到在不改動 HTTP 的語義,HTTP 方法、狀態碼、URI 及首部字段的情況下,突破 HTTP1.1 的性能限制,改進傳輸性能,實現低延遲和高吞吐量。

在二進制分幀層上,HTTP2.0 會將所有傳輸的信息分割爲更小的消息和幀,並對它們採用二進制格式的編碼,其中HTTP1.x 的首部信息會被封裝到 Headers 幀,而我們的 request body 則封裝到 Data 幀裏面。

首部字段壓縮

HTTP2.0 使用 HPACK 算法對首部字段的數據進行壓縮,這樣數據體積小了,在網絡上傳輸就會更快。

首部字段差異傳輸

HTTP2.0 規定了在客戶端和服務器端會使用並且維護首部表來跟蹤和存儲之前發送的鍵值對,對於相同的頭部,不必再通過請求發送,只需發送一次。

事實上,如果請求中不包含首部字段(例如對同一資源的輪詢請求),此時服務器自動使用之前請求發送的首部字段,那麼首部字段開銷就是零字節。

如果首部發生變化了,那麼只需要發送變化了數據在 Headers 幀裏面,新增或修改的首部幀會被追加到首部表首部表在 HTTP2.0 的連接存在期內始終存在,由客戶端和服務器共同漸進地更新。

使用 CDN

嚴格意義上,CDN 不算 Http 優化,前端也無法直接處理這個事情,這是運維的事。CDN 節點可以解決跨運營商和跨地域訪問的問題,提升訪問速度。

CDN的全稱是 Content Delivery Network,即內容分發網絡。CDN 是構建在網絡之上的內容分發網絡,依靠部署在各地的邊緣服務器,通過中心平臺的負載均衡、內容分發、調度等功能模塊,使用戶就近獲取所需內容,降低網絡擁塞,提高用戶訪問響應速度和命中率。CDN 的關鍵技術主要有內容存儲和分發技術。—— 科學百科

CDN 有什麼優勢?

CDN 最大的優勢在於提升用戶資源訪問速度,因此靜態資源走 CDN 是一個很好的優化點。
分佈式服務器,用戶就近訪問,CDN 節點可以解決跨運營商和跨地域訪問的問題,同時分散源服務器訪問壓力。

還有一個而外的優點:
無 cookie 傳輸(其實這個不完全算是優勢)。靜態資源一般無需 cookie,靜態資源放在不同域名可以減少一定程度的帶寬和提升一定的訪問速度,雖然單個請求不明顯,但是量多了是會有質的區別的。

CDN 是如何分散源服務器的壓力的?

CDN 的核心點有兩個: 一個是緩存,一個是回源。

通過緩存和回源策略,達到分散源服務器的壓力。首先將從根服務器請求來的資源按要求緩存。然後當有用戶訪問某個資源的時候,如果被解析到的那個 CDN 節點沒有緩存響應的內容,或者是緩存已經到期,就會回源站去獲取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章