前端效能優化:網站效能優化

2022-09-13 09:00:32

網站效能優化可以從下面總結點入手。

1. 減少HTTP請求

  • 使用雪碧圖 - CSS Sprites,把多個圖片合併到一個單獨的圖片中,利用CSS -background-position調整圖片顯示位置。這種方式適用面比較廣泛。
    缺點是,如果一張小圖,需要N個顏色,就必須做N個不同顏色的小圖,合併到大圖裡面。
  • 使用data:URL展示圖片,它可以在頁面中渲染圖片但無需額外的HTTP請求,請求格式:
<img scr="data:image/jpg;base64, xxxxxxxxxxxxxxxx">

 

缺點是:此方案不適合mobile應用;IE7以下不支援;如果一張圖片在多個頁面被用到,無法利用瀏覽器快取。
為了解決無法快取問題,可以將data:image應用到CSS樣式中,比如:

.imageA {
 background-image: url(data:image/jpg;base64, xxxxxxxxxxxxxxxx);
}

 

  • 合併指令碼和樣式表
  • Multipart XHR
    執行使用者端用一個HTTP請求就可以從伺服器端傳遞多個資源。它通過在伺服器端將資源(CSS檔案,HTML片段,Javascript程式碼或者base64編碼的圖片)打包成一個由雙方約定的字串分割的長字串,並行送到使用者端。
    然後用Javascript程式碼處理這個長字串,並根據他的mime-type型別和傳入的其他‘頭資訊’解析出每個資源。
    例如,解析一串圖片編碼,輸入為req.responseText
function splitImages(imageString){
         var imageData = imageString.split('\u0001');
         var imageElement;

         for (var i =0, len = imageData.length; i<len; i++){
           imageElement = document.createElement('img');
           imageElement.src = 'data:image/jpeg;base64,' + imageData[i];
           document.getElementById('container').appendChild(imageElement);
         }
}

 

2. 使用CDN

內容釋出網路(CDN)是一組分佈在多個不同地理位置的WEB伺服器,用於更加有效地向用戶釋出內容。
CDN用於釋出靜態內容,如圖片,指令碼,樣式表和Flash。

不使用CDN時:

  1. 使用者在瀏覽器存取欄中輸入要存取的域名。
  2. 瀏覽器向DNS伺服器請求對該域名的解析。
  3. DNS伺服器返回該域名的IP地址給瀏覽器。
  4. 瀏覽器使用該IP地址向伺服器請求內容。
  5. 伺服器將使用者請求的內容返回給瀏覽器。

儘量將CDN的域名設定的不同於請求方網站的域名。比如,網站為a.com,CDN域名可以設定為acdn.com。為什麼呢?

  • Cookie隔離:Cookie 是緊跟域名的,同一個域名下的所有請求,都會攜帶 Cookie。試想,海量請求圖片或JS/CSS檔案時,還要攜帶Cookie,也會成為不小的開銷。

  • 並且,瀏覽器在同一個時刻向同一個域名請求檔案的並行下載數量是有限的(Chrome為6個並行),所以,可以利用多個域名主機存放不同的靜態資源,增大頁面載入時資源並行下載數量。

3. 利用HTTP快取

具體內容參考文章 

https://www.cnblogs.com/yizhiamumu/p/16687989.html

4. 壓縮元件

開啟HTTP Gzip壓縮。

request: Accept-Encoding: gzip, deflate
response:Content-Encoding:gzip

 

5. 將樣式表放在頂部

外部指令碼檔案和CSS檔案是並行下載的,把樣式表在頁面中的位置並不影響下載時間,但會影響頁面的呈現!瀏覽器必須要等樣式表載入完畢之後才渲染頁面
因此,應該把樣式表放在head中,這樣它就能被最先下載使頁面逐步呈現。

6. 將JS指令碼放在底部

一般,JS指令碼是被禁止並行下載的,因為JS指令碼可能使用document.write來修改頁面內容,所以必須保證JS執行順序。
指令碼下載後,必須執行完,才可以繼續後面的解析。

但是,Chrome瀏覽器支援並行下載資原始檔,並保證按順序執行(參考《WebKit技術內幕-朱永盛》)。

7. 避免CSS表示式

CSS表示式是動態設定CSS屬性的一種強大(並且危險)的方式。CSS表示式求值頻率比人們期望的要高,它們不只在頁面呈現和大小變化時求知,甚至使用者滑鼠在頁面上拖拽都要求知。
如,將背景色設定為每小時變化一次:

background-color:expression((new Date()).getHours()%2?"#ccc":"#000");

 

觸發頻率太高!不建議使用。

8. 使用外部JS和CSS

純粹來講,內聯的JS和CSS可以產生比外部檔案檔案更快的響應速度。
但是現實中,外部連結的JS和CSS檔案會產生較快的頁面,是因為JS和CSS檔案有可能被快取

9. 減少DNS查詢

DNS也是開銷。通常瀏覽器查詢一個給定主機名的IP地址要花費20~120毫秒。在DNS查詢完成之前,瀏覽器不能從主機名哪裡下載任何東西。
只要cline-server之間保持TCP連線開啟狀態,就無需DNS查詢。所以,我們可以通過使用Keep-Alive和較少的域名來減少DNS查詢。

Keep-Alive,HTTP1.1協定中推出的持久連線。特點為:只要任意一端沒有明確提出斷開連線,則保持TCP連線狀態。

含有Keep-Alive首部的response範例:

HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Thu, 11 Aug 2016 15:23:13 GMT
Keep-Alive: timeout=5, max=1000
Last-Modified: Mon, 25 Jul 2016 04:32:39 GMT
Server: Apache

 

10. 壓縮JavaScript和CSS

可以用各類構建或者編譯工具壓縮指令碼和樣式檔案,比如:gulpwebpack

11. 少用iframe

iframe是開銷最高的DOM元素,它的缺點遠大於優點。

  • 不利於SEO:搜尋引擎的檢索程式無法解讀iframe中的src
  • 阻塞onload事件:iframe不載入完畢,就不會觸發父視窗的onload事件。
  • 影響頁面資源並行載入:iframe和主頁面共用連線池,而瀏覽器對相同域的連線有限制,所以會影響頁面資源的並行載入。
    為了解決兩個問題,可以動態設定iframe中的src屬性,程式碼如下:
<iframe id="iframe1" src=""></iframe>

<script>
document.getElementById('iframe1').src = "www.api.a.com";
</script>

 

12.少用Table

table內容渲染是將table的DOM渲染樹全部生成完並一次繪製到頁面上,所以,在渲染長表格時很耗效能,應該儘量避免使用。

可以使用uldiv替代。

13. JS檔案非同步/按需載入

有多種方式支援JavaScript非同步載入。

  • Script DOM Element
    這恐怕是最常見的非同步載入指令碼方法,即,動態建立一個script標籤,並設定其src值。如下:
function createScript(url){
  var scrElem = document.createElement('script');
  srcElem.src = url;
  document.getElementsByTagName('head')[0].appendChild(scrElem);
}

 

優點:支援跨域載入指令碼檔案;相容性最好、普適性最高的方案
缺點:指令碼無序執行;會阻塞onload事件

  • XMLHttpRequest

通過XMLHttpRequest的方式下載指令碼檔案,然後使用eval或者動態新增<script>標籤並設定其text屬性來執行指令碼。

// 不考慮IE
var xhrObj = new XMLHttpRequest();
xhrObj .onreadystatechange = function(){
  if (xhrObj .readyState == 4) {
    // 方式一
    eval(xhrObj.responseText);  
    // 方式二
    var scrElem = document.createElement('script');
    srcElem.text= xhrObj.responseText;
    document.getElementsByTagName('head')[0].appendChild(scrElem);
  }
}
xhrObj .open('GET', 'a.js', true);
xhrObj .send('');

 

優點:將指令碼下載和指令碼執行分離開,可以在適當的時候再執行指令碼;不會阻塞onload事件
缺點;通過XMLHttpRequest獲取的指令碼檔案必須和主頁面是同一個域名下。也就是說,不支援跨域下載指令碼(除非做跨域處理)。因此不適合載入第三方檔案;指令碼無序執行。

  • defer和async

兩者都支援非同步載入檔案,不同之處是,defer會在全部資源下載完畢後才執行JS檔案;async在指令碼檔案下載完就立刻執行,並且,async模式載入的JS檔案無法依序執行,對於有順序依賴的指令碼來說,不應該採用這種方式。
defer相對友好一些,並可以保證JS檔案按照順序執行

<script src="a.js" defer></script>
<script src="a.js" async></script>

 

defer和async優點:支援跨域載入指令碼檔案。
defer優點:可以保證JS檔案按照順序執行。

defer和async缺點:IE10以上(包括IE10)才支援。
async缺點:JS檔案無法依序執行;會阻塞onload事件

14. 圖片懶載入

通過圖片懶載入可以讓一些不可視的圖片不去載入,避免一次性載入過多的圖片導致請求阻塞(瀏覽器一般對同一域名下的並行請求的連線數有限制),這樣就可以提高網站的載入速度,提高使用者體驗。

實現方案一

第一步: 懶載入的img標籤的src設定縮圖或者不設定src,然後自定義一個屬性,值為真正的圖片或者原圖的地址(比如data-src)。

// https://a.com/logo.png 是圖片的真實地址,設定到data-src屬性上。
<img data-src="https://a.com/logo.png" class="lazy-image"/> 
// css部分 
.lazy-image { 
    background: url('loading.gif') no-repeat center; 
} 

 

第二步:頁面載入完後,獲取所有需要懶載入的圖片的元素集合,判斷是否在可視區域,如果是在可視區域的話,設定元素的src屬性值為真正圖片的地址。

// 監聽捲動事件
document.addEventListener('scroll', inViewShow);
// 顯示圖片
inViewShow() {     
    let imageElements = Array.prototype.slice.call(document.querySelectorAll('.lazy-image'))    
    let len = imageElements.length     
    for(let i = 0; i < len; i++) {         
        let imageElement = imageElements[i]        
        const rect = imageElement.getBoundingClientRect() // 出現在視野的時候載入圖片         
        if(rect.top < document.documentElement.clientHeight) {             
            imageElement.src = imageElement.dataset.src // 賦值到真正的src上           
            imageElements.splice(i, 1)             
            len--             
            i--         
        }     
    } 
}

 

實現方案二

利用瀏覽器新特性IntersectionObserver

IntersectionObserver介面提供了一種非同步觀察目標元素與其祖先元素或頂級檔案視窗(viewport)交叉狀態的方法)。

// 渲染
public render() {
    const { src } = this.state
    // 一旦src變化,更新為img標籤,載入圖片
    if (src) {
      return <img {...this.props} src={src} />
    }
   // 佔點陣圖片
    return (
      <span ref={(ele) => (this.ele = ele)}  />
    )
  }

// 監控
public componentDidMount() {
    // 判斷瀏覽器是否支援IntersectionObserver函數
    if (typeof IntersectionObserver === 'function') {
      const cb = (entries) => {
        if (entries.some((item) => item.intersectionRatio > 0)) {
          const { src } = this.props
          this.setState({ src })
          this.ob.disconnect() // 取消監聽
        }
      }
      this.ob = new IntersectionObserver(cb, {
        root: document.body, // 祖先元素
        threshold: [0, 0.01], // 交叉值
      })
      this.ob.observe(this.ele)
    } else {
      const { src } = this.props
      this.setState({ src })
    }
 }

 

實現方案三

使用三方庫處理圖片懶載入。

15. 避免頁面中空的href和src

link標籤中的href,或者iframscriptimg標籤的src屬性為空時,瀏覽器在渲染過程中仍然會將hrefsrc中的空內容進行載入,直到失敗為止。這樣會阻塞頁面中其他資源的下載過程。

16. 減少頁面重定向

頁面重定向會延長頁面內容返回的等待時間,一次重定向大致需要600毫秒。

17. 使用HTTP2.0

相比HTTP1.1,2.0版本有了更強大的能力,可以提升傳輸效能。

  • TCP多路複用能力:這和HTTP1.1的連線複用還是不同的。HTTP1.1的連線複用(keep-alive)為應用層,指HTTP複用TCP連線後,可以序列傳送多個HTTP請求。而HTTP2.0多路複用在傳輸層,幀的多路複用,即不同檔案的傳輸幀可以在一個TCP連線中同時傳輸,也就是多個HTTP請求並行傳送。
  • 採用二進位制格式傳輸資料,非HTTP 1.x的文字格式(預設)。且採用HPACK壓縮傳輸,最大限度節省了寬頻。
  • 支援傳輸流的優先順序和流量控制:伺服器端可以優先輸出CSS檔案,而不必依賴HTML中定義的指令碼載入順序。
 
image.png

18. 預載入dns-prefetchpreconnectprefetchprerender

dns-prefetch:使瀏覽器主動去執行域名解析。

<link rel="dns-prefetch" href="https://fonts.google.com/" >

 

preconnect:提前建立連線。比dns-prefetch多走了兩步,除了完成dns解析之外,還完成了TCP握手(HTTPS下還完成TLS握手)。

<link rel="preconnect" href="https://fonts.google.com/" >

  

prefetch:讓瀏覽器預載入一個資源(HTML,JS,CSS或者圖片等),可以讓使用者跳轉到其他頁面時,響應速度更快。

<link rel="prefetch" href="https://css-tricks.com/a.png">

  

prerender:不僅會載入資源,還會解執行頁面,進行預渲染(太重,不建議使用)

<link rel="prefetch" href="https://css-tricks.com/a.html">