Web性能優化筆記

網頁性能優化的目的

  1. 減小服務器壓力
  2. 加強用戶體驗,減小加載時間,通俗地說就是用戶感受打開你的網頁很快,等待頁面資源加載的時間很短

方法

回答這題的思路要從另外一道面試題:從敲回車開始到頁面展示這個過程發生了什麼入手。如下是其中的一些過程:css

  1. 看請求的html頁面有沒緩存,沒有就進行下面步驟
  2. dns查詢
  3. 創建TCP鏈接
  4. 發送HTTP請求
  5. 後臺處理
  6. 接收響應
  7. 接收完成
  8. 讀取html的DOCTYPE
  9. 逐行解析
  10. 看到<link href='#'>,<script src='#'>就會像服務器發起請求

優化以上每一個步驟,這樣子整個web網頁的性能就提升了html

減小dns查詢

你在瀏覽器地址欄輸入網址,經過DNS查詢獲得網站真實IP。前端

好比你的一個網站,須要向 baidu.com 請求一個js文件,向 bootcdn.cn 請求一個css文件,這樣子就要進行2次dns查詢(沒有dns緩存的狀況下),解決方法就是各類資源放在數量儘可能少的域名下java

域名解析的流程

  1. 瀏覽器緩存 – 瀏覽器會緩存DNS記錄一段時間
  2. 系統緩存 - 從 Hosts 文件查找是否有該域名和對應 IP。
  3. 路由器緩存 – 通常路由器也會緩存域名信息。
  4. ISP DNS 緩存 – 好比到電信的 DNS 上查找緩存。
  5. 若是都沒有找到,則向根域名服務器查找域名對應 IP,根域名服務器把請求轉發到下一級,知道找到 IP

把資源分散到不一樣的域名

把資源分散到不一樣的域名容許你最大化並行下載數git

例如chrome,只能同時向一個域名最多發8個請求,把資源分散到不一樣的域名容許你最大化並行下載數,但這樣子dns查詢時間就變長,和前面的「減小dns查詢「衝突了github

若是文件不多,例如只有3個文件,放在1個域名下,這樣dns查詢時間就很快,此時若是把這3個文件分散到更多的域名下,速度也不會提升,由於3個請求尚未達到chrome的只能同時向一個域名最多發請求的數量的極限web

若是文件不少,例如16個文件,還所有放在1個域名下,雖然dns查詢時間很快,但請求時間會很慢,由於只能同時向一個域名最多發8個請求,那另外8個請求要等前面的請求發送完才能再發送面試

懶加載和預加載

懶加載就是例如打開一個網頁,viewport如下的網頁內容先不加載,等到用戶滾動看到那部份內容再加載相應的資源,好處:節省CDN費用,下降服務器壓力chrome

預加載就是預先加載用戶即將要看到的內容,例如當你在第一頁時就預先加載第二頁的內容,這樣子用戶打開第二頁的時候就不用等待加載時間npm

使用CDN

cdn即內容分發網絡,例如谷歌的主服務器1在美國,在北京有個服務器2,服務器2不斷地把主服務器1上的內容複製過來(可能說得不是很準確),這樣子在北京訪問谷歌,直接訪問北京的服務器,比你訪問美國的主服務器要快

傳輸時gzip壓縮資源

HTTP/1.1開始,客戶端經過http請求中的Accept-Encoding頭部來提示支持的壓縮:

Accept-Encoding: gzip, deflate

若是服務器看到這個頭部,它可能會選用列表中的某個方法壓縮要請求的資源。服務器經過響應中的Content-Encoding頭部提示客戶端:

Content-Encoding: gzip

客戶端接收到響應後會解壓縮

gzip通常可減少響應的70%。儘量去gzip更多(文本)類型的文件。html,腳本,樣式,xml和json等等都應該被gzip,而圖片,pdf等等不該該被gzip,由於它們自己已被壓縮過,gzip它們只是浪費cpu,甚至增長文件大小。

Cache-Control

//後臺代碼
 if (path  ==='/js/main.js') {
   let  string  =    fs.readFileSync('./js/main.js', 'utf8');
   response.setHeader('Content-Type',   'application/javasript;charset=utf-8');
  response.setHeader('Cache-Control',   'max-age=30');
  response.write(string);
  response.end()
}

max-age=30指當第一次請求並下載/js/main.js後,30s內遇到一樣/js/main.js的請求(例如刷新當前頁面),會從緩存裏直接讀取main.js,不會再向服務器發請求

靜態資源:將 Expires 響應頭設置爲未來很遠的時間,實現「永不過時」策略;
動態資源:設置合適的 Cache-Control 響應頭,讓瀏覽器有條件地發起請求。

ETag

在典型用法中,當一個URL被請求,Web服務器會返回資源和其相應的ETag值,它會被放置在HTTP的「ETag」字段中:

ETag: "686897696a7c876b7e"

而後,客戶端能夠決定是否緩存這個資源和它的ETag。之後,若是客戶端想再次請求相同的URL,將會發送一個包含已保存的ETag和「If-None-Match」字段的請求。

If-None-Match: "686897696a7c876b7e"

客戶端請求以後,服務器可能會比較客戶端的ETag和當前版本資源的ETag。若是ETag值匹配,這就意味着資源沒有改變,服務器便會發送回一個極短的響應,包含HTTP 「304 未修改」的狀態。304狀態告訴客戶端,它的緩存版本是最新的,並應該使用它。

然而,若是ETag的值不匹配,這就意味着資源極可能發生了變化,那麼,一個完整的響應就會被返回,包括資源的內容,就好像ETag沒有被使用。這種狀況下,客戶端能夠用新返回的資源和新的ETag替代先前的緩存版本。

部分的後臺Node.js示例代碼:

clipboard.png

上圖代碼的md5(string)能輸出string的md5值,用的是https://www.npmjs.com/package... ,170k指的是string的大小

用沒有cookie的域名提供資源

當瀏覽器請求靜態圖片並把cookie一塊兒發送到服務器時,cookie此時對服務器沒什麼用處。因此這些cookie只是增長了網絡流量。因此你應該保證靜態資源的請求是沒有cookie的。能夠建立一個子域名來託管全部靜態組件。

好比,你域名是www.example.org,能夠把靜態資源託管在static.example.org。不過,你若是把cookie設置在頂級域名example.org下,這些cookie仍然會被傳給static.example.org。這種狀況下,啓用一個全新的域名來託管靜態資源

另一個用沒有cookie的域名提供資源的好處是,某些代理可能會阻止緩存待cookie的靜態資源請求。

減小cookie體積

  1. 後端代碼經過 Set-Cookie 響應頭設置 Cookie的內容
  2. 瀏覽器獲得 Cookie 以後,每次訪問相同域名的網頁時, 每次請求的header都會帶上 Cookie,例如

Cookie:sessionId=44438.40790818172

http cookie的使用有多種緣由,好比受權和個性化。cookie的信息經過http頭部在瀏覽器和服務器端交換。儘量減少cookie的大小來下降響應時間。

  • 消除沒必要要的cookie。
  • 儘量減少cookie的大小來下降響應時間。
  • 注意設置cookie到合適的域名級別,則其它子域名不會被影響。

選擇<link>而不是@import

以前的一個最佳原則是說CSS應該在頂部來容許逐步渲染。

在IE用@import和把CSS放到頁面底部行爲一致,因此最好別用

減小/最小化 http 請求數。

有如下幾個技術:

  • Combined files。合併文件,如合併js,合併css都能減小請求數。若是頁面間腳本和樣式差別很大,合併會更具挑戰性。
  • CSS Sprites。雪碧圖能夠合併多個背景圖片,經過background-imagebackground-position 來顯示不一樣部分。
  • Inline images。使用data:url scheme來內連圖片。

使用外部JS和CSS。

真實世界中使用外部文件通常會加快頁面,由於JS和CSS文件被瀏覽器緩存了。內連的JS和CSS怎在每次HTML文檔下載時都被下載。內連減小了http請求,但增長了HTML文檔大小。另外一方面,若是JS和CSS被緩存了,那麼HTML文檔能夠減少大小而不增長HTTP請求。

核心因素,就是JS和CSS被緩存相對於HTML文檔被請求的頻率。儘管這個因素很難被量化,但能夠用不一樣的指標來計算。若是網站用戶每一個session有多個pv,許多頁面重用相同的JS和CSS,那麼有很大可能用外部JS和CSS更好。

惟一例外是內連更被主頁偏心,如http://www.yahoo.com/
主頁每一個session可能只有少許的甚至一個pv,這時候內連可能更快

對多個頁面的首頁來講,能夠經過技術減小(其它頁面的)http請求。在首頁用內連,初始化後動態加載外部文件,接下來的頁面若是用到這些文件,就可使用緩存了。

壓縮JS和CSS。

壓縮就是刪除代碼中沒必要要的字符來減少文件大小,從而提升加載速度。當代碼壓縮時,註釋刪除,不須要的空格(空白,換行,tab)也被刪除。

混淆是對代碼可選的優化。它比壓縮更復雜,而且可能產生bug。在對美國top10網站的調查,壓縮可減少21%,而混淆可減少25%。

除了外部腳本和樣式,內連的腳本和樣式一樣應該被壓縮。

使用事件委託

有時候頁面看起來不那麼響應(響應速度慢),是由於綁定到不一樣元素的大量事件處理函數執行太屢次。這是爲何要使用事件委託

另外,你沒必要等到onload事件來開始處理DOM樹,DOMContentLoaded更快。大多時候你須要的只是想訪問的元素已在DOM樹中,因此你沒必要等到全部圖片被下載。

優化圖片

在設計師建好圖片後,在上傳圖片到服務器前你仍能夠作些事:

  • 檢查gif圖片的調色板大小是否匹配圖片顏色數。
  • 能夠把gif轉成png看看有沒有變小。除了動畫,gif通常能夠轉成png8。
  • 運行pngcrush或其它工具壓縮png。
  • 運行jpegtran或其它工具壓縮jpeg。

TCP 鏈接複用

在 HTTP/1.0 時代,每個請求都會從新創建一個 TCP 鏈接,一旦響應返回,就關閉鏈接。 而創建一個鏈接,則須要進行三次握手(https的話則是9次握手),這極大的浪費了性能

所以 HTTP/1.1 新增了「keep-alive」功能,當瀏覽器創建一個 TCP 鏈接時,新的請求能夠在上次創建的tcp鏈接之上發送,鏈接能夠複用。。(現現在大多數瀏覽器默認都是開啓的)

HTTP 2.0 多路複用

HTTP/2.0 時代擁有了「多路複用」功能,意思是: 在一條鏈接上,我能夠同時發起無數個請求,而且響應能夠同時返回。

多個鏈接優化

問題:一個1M的JS文件,如何下載比較快?

  1. 整個文件一個JS
  2. 拆分紅兩個500M的JS
  3. 拆分紅4個250M的JS

理論上答案是3.這樣能夠並行下載4個文件,減小了帶寬的佔據,可是增長了3次TCP請求時間,在下載時間和TCP請求時間的取捨上,就要具體狀況具體測試分析了。

若是是移動端就選擇方案1,由於移動端對多文件下載不友好

若是選擇第3種方案,那麼引生出另外一個問題:爲何不分得更多?
答:每一個瀏覽器對並行下載有上限。每一個域名限制最多同時進行N個下載線程。

其餘

<link href=''><style><head>裏面,由於不管放在那裏都會阻塞html渲染(等到css文件下載並解析完纔會顯示html內容),因此還不如放head裏面讓css文件儘早下載

js放body最後,這樣子能夠獲取html節點,且能先儘早顯示html頁面

chrome devtool的audits panel能夠檢測網站的性能優化狀況

參考資源:

相關文章
相關標籤/搜索