單頁面系統的一些性能優化


 

1:初次進入系統的新用戶

首次進入該系統的用戶,沒有任何的文件緩存。進入系統后加載index.html 會將所有的文件(圖片、js、css)下載下來 耗時在3.3s左右。之后進入系統 只會額外加載一些頁面的圖片,不會再加載js、css文件。

 

2:已經進過該系統的老用戶,且系統未更新之前

用戶本地的文件都已經被緩存(圖片、css、js),在系統更新之前,所有的文件都沒有更改,用戶再進入系統時,速度會在500ms之內,可以從下圖看到,所有的文件加載速度都是為0的。

 

3:已經進入過該系統的老用戶,且系統已經更新

分析:

用戶訪問系統時會下載index.html文件,此時,如果index里引入的文件(圖片、js、css)的文件名沒有改變,客戶端(瀏覽器)會仍然從緩存中取數據。但每次迭代更新都會更改css、js,每次的改動都會導致再次打包時該文件的文件名發生改變,此時,用戶就會重新下載這個被改動的文件。如果被改動的文件的大小比較大,那么該用戶仍然會在再次進入該系統時花費較多的時間從服務器下載該文件,但介於有很多資源並不會改變,比如引入的一些插件(包含js、css、圖片)從始至終並不會去改變的東西,那么用戶首次進入該系統時就已經緩存,一直不需要從服務器下載。

4:關於打包

不管如何打包,系統的所有文件都會被瀏覽器下載,能做的有以下幾點。

1:縮小所有的文件(js、圖片、css)

Js/css:能復用的盡量復用,不寫冗余的代碼。(注釋,空格會在打包時自動去除進行壓縮,所以注釋可以多寫)

html:我們將所有的html都打包進了js文件,這樣可以在改變html時更改js文件名,這樣在系統更新之后,老用戶可以重新下載html,一次性下載,之后不會在從服務器下載html

針對於以上的機制,用戶在登錄系統期間,即使我們將服務器上的所有的代碼刪除掉,用戶仍然可以在系統之內正常運行,因為用戶已經下載了index頁面及這個index頁面所引入的各個文件。

5:現在的打包情況

grunt的配置方法:

先將ngtemplete任務刪除,然后filerev(為文件提供md5值)會根據usemin配置的文件指向,將所指向的文件加上md5值,這時候所有的html里引用的文件名會根據打包之后的名字而進行替換。

 

測試方法:

  1:進入系統后,先將所有的html都點擊一次,這樣,就保證了所有的html文件都被瀏覽器所下載

    此時,用戶不關閉窗口,將服務器上的前端文件全部刪除,用戶仍然可以正常操作該系統。如果用戶之前有一個頁面從來沒有使用過,即沒有請求后台拿到數據后緩存在瀏覽器內這一步操作。將會得到一個404的頁面返回。如下圖,我在錄單頁面(正常使用)去訪問一個從來沒使用過的頁面(發車確認)就會發生404請求。

 

     原因:html、js、css都已經被客戶端下載,客戶端可以正常使用並且訪問服務器。  

  2:關閉瀏覽器窗口,重新進入系統。

    這時用戶是無法訪問到我們的網頁的。(這里牽涉到客戶端的一些關閉、刷新等操作對緩存的影響)

    1. 瀏覽器地址欄中寫入URL,回車
      瀏覽器發現緩存中有這個文件了,不用繼續請求了,直接去緩存拿。(最快)
    2. F5
      F5就是告訴瀏覽器,別偷懶,好歹去服務器看看這個文件是否有過期了。於是瀏覽器就發送一個請求帶上If-Modify-since。
    3. Ctrl+F5
      告訴瀏覽器,你先把你緩存中的這個文件給我刪了,然后再去服務器請求個完整的資源文件下來。於是客戶端就完成了強行更新的操作.

    此時會產生的緩存問題:

    問題1:直接寫入url,回車,這個時候會發現,用戶是從緩存中取出的index.html,這樣一來,及時我們升級了系統,老用戶也看不到新的系統,這個時候只能選擇刷新。

    2:f5刷新:

     刷新時,大家可以看到任然是緩存,但是狀態是304,說明這時是請求了服務器。優化歷程中,我們在線上上的一個版本是在退出時默認刷新了頁面,這樣就解決了問題1。

     后來我們選擇了在退出的時候將地址重新定向,在index.html后面加上了一個隨機數,這樣也就相當於重新打開了一個頁面

    3:ctrl+f5

    這種就是完全拋棄了緩存,完全從服務器上下載資源,不符合我們的預期。

現在的打包選擇了:

1: html從后台獲取,這樣就會減少新用戶首次加載網頁時的下載量的大小。

2:對於echart.min.js與viewer.min.js采用了異步加載方式,這樣可以減少js的加載對dom的渲染的阻塞,可以使用戶更快的見到我們的頁面,減少空白頁面的持續時間。

3:退出時 刷新頁面,重新加載了index,index里引入的html因為不是以js做的緩存,所以會跟服務器通信。可以實時監控到html的文件變化

4:對於js,css文件。因為服務端設置的是緩存時間為0,所以每次進入系統都會跟服務器做通信,index頁面的重新加載,會監控到js文件的變化。之所以在chrome上看到的200狀態,其實也是做了通信的,只是chrome將其與304狀態混淆了,其實還是有通信。.

5:前端index文件加上了nocache meta 保證每次先跟服務端通信再加載。

6:瀏覽器緩存狀態的說明

 

 

每個狀態的詳細說明如下:

 

1Last-Modified

在瀏覽器第一次請求某一個URL時,服務器端的返回狀態會是200,內容是你請求的資源,同時有一個Last-Modified的屬性標記(HttpReponse Header)此文件在服務期端最后被修改的時間,格式類似這樣:

Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT

客戶端第二次請求此URL時,根據HTTP協議的規定,瀏覽器會向服務器傳送If-Modified-Since報頭(HttpRequest Header),詢問該時間之后文件是否有被修改過:

If-Modified-Since:Tue, 24 Feb 2009 08:01:04 GMT

如果服務器端的資源沒有變化,則自動返回HTTP304(NotChanged.)狀態碼,內容為空,這樣就節省了傳輸數據量。當服務器端代碼發生改變或者重啟服務器時,則重新發出資源,返回和第一次請求時類似。從而保證不向客戶端重復發出資源,也保證當服務器有變化時,客戶端能夠得到最新的資源。

注:如果If-Modified-Since的時間比服務器當前時間(當前的請求時間request_time)還晚,會認為是個非法請求

 

2Etag工作原理

HTTP協議規格說明定義ETag為“被請求變量的實體標記”(參見14.19)。簡單點即服務器響應時給請求URL標記,並在HTTP響應頭中將其傳送到客戶端,類似服務器端返回的格式:

Etag:“5d8c72a5edda8d6a:3239″

客戶端的查詢更新格式是這樣的:

If-None-Match:“5d8c72a5edda8d6a:3239″

如果ETag沒改變,則返回狀態304。

:在客戶端發出請求后,HttpReponse Header中包含Etag:“5d8c72a5edda8d6a:3239″

標識,等於告訴Client端,你拿到的這個的資源有表示ID:5d8c72a5edda8d6a:3239。當下次需要發Request索要同一個URI的時候,瀏覽器同時發出一個If-None-Match報頭(Http RequestHeader)此時包頭中信息包含上次訪問得到的Etag:“5d8c72a5edda8d6a:3239″標識。

If-None-Match:“5d8c72a5edda8d6a:3239“

,這樣,Client端等於Cache了兩份,服務器端就會比對2者的etag。如果If-None-Match為False,不返回200,返回304(Not Modified) Response。

 

3Expires

給出的日期/時間后,被響應認為是過時。如Expires:Thu, 02 Apr 2009 05:14:08 GMT

需和Last-Modified結合使用。用於控制請求文件的有效時間,當請求數據在有效期內時客戶端瀏覽器從緩存請求數據而不是服務器端.當緩存中數據失效或過期,才決定從服務器更新數據。

 

4Last-ModifiedExpires

Last-Modified標識能夠節省一點帶寬,但是還是逃不掉發一個HTTP請求出去,而且要和Expires一起用。而Expires標識卻使得瀏覽器干脆連HTTP請求都不用發,比如當用戶F5或者點擊Refresh按鈕的時候就算對於有Expires的URI,一樣也會發一個HTTP請求出去,所以,Last-Modified還是要用的,而且要和Expires一起用。



5EtagExpires

如果服務器端同時設置了Etag和Expires時,Etag原理同樣,即與Last-Modified/Etag對應的HttpRequestHeader:If-Modified-Since和If-None-Match。我們可以看到這兩個Header的值和WebServer發出的Last-Modified,Etag值完全一樣;在完全匹配If-Modified-Since和If-None-Match即檢查完修改時間和Etag之后,服務器才能返回304.



6Last-ModifiedEtag

分布式系統里多台機器間文件的last-modified必須保持一致,以免負載均衡到不同機器導致比對失敗

分布式系統盡量關閉掉Etag(每台機器生成的etag都會不一樣)

Last-Modified和ETags請求的http報頭一起使用,服務器首先產生Last-Modified/Etag標記,服務器可在稍后使用它來判斷頁面是否已經被修改,來決定文件是否繼續緩存

過程如下:

1.客戶端請求一個頁面(A)。

2.服務器返回頁面A,並在給A加上一個Last-Modified/ETag。

3.客戶端展現該頁面,並將頁面連同Last-Modified/ETag一起緩存。

4.客戶再次請求頁面A,並將上次請求時服務器返回的Last-Modified/ETag一起傳遞給服務器。

5.服務器檢查該Last-Modified或ETag,並判斷出該頁面自上次客戶端請求之后還未被修改,直接返回響應304和一個空的響應體。

注:

1、Last-Modified和Etag頭都是由WebServer發出的HttpReponse Header,WebServer應該同時支持這兩種頭。

2、WebServer發送完Last-Modified/Etag頭給客戶端后,客戶端會緩存這些頭;

3、客戶端再次發起相同頁面的請求時,將分別發送與Last-Modified/Etag對應的HttpRequestHeader:If-Modified-Since和If-None-Match。我們可以看到這兩個Header的值和WebServer發出的Last-Modified,Etag值完全一樣;

4、通過上述值到服務器端檢查,判斷文件是否繼續緩存;

 

 

7、關於 Cache-Control: max-age=秒 和 Expires

Expires = 時間,HTTP 1.0 版本,緩存的載止時間,允許客戶端在這個時間之前不去檢查(發請求)
max-age = 秒,HTTP 1.1版本,資源在本地緩存多少秒。
如果max-age和Expires同時存在,則被Cache-Control的max-age覆蓋。

Expires 的一個缺點就是,返回的到期時間是服務器端的時間,這樣存在一個問題,如果客戶端的時間與服務器的時間相差很大,那么誤差就很大,所以在HTTP 1.1版開始,使用Cache-Control: max-age=秒替代。

Expires =max-age +   “每次下載時的當前的request時間”

所以一旦重新下載的頁面后,expires就重新計算一次,但last-modified不會變化 

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM