vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案

trsoliu發表於2018-12-10
  1. 問題背景
    a.瀏覽器端在每次釋出新的版本時候,總會出現因為單頁面專案中index.html檔案(200 ok from disk cache 不傳送請求,直接取用了本地磁碟快取)和服務端版本不一致的問題,導致使用者不能及時更新,需要通過手動重新整理來強制從服務端更新檔案。
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案

    b.補充一下,服務端和瀏覽器之間架構。

    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案

  2. 三級快取問題
    上述,我們看到瀏覽器沒有發出請求直接從磁碟中取出index.html檔案,這其實是二級快取。 
    當瀏覽器訪問的伺服器沒有設定快取時,瀏覽器先200 OK from memory cache,如果瀏覽器快取中沒有該檔案,那麼瀏覽器會200 OK from disk cache 從磁碟中找改檔案,如果還找不到,瀏覽器會從服務端請求該檔案下載使用。

  3. Last-Modified/If-Modified-Since
    a.當瀏覽器第一次請求一個url時,伺服器端的返回狀態碼為200,同時HTTP響應頭會有一個Last-Modified標記著檔案在伺服器端最後被修改的時間。
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案
    b.瀏覽器第二次請求上次請求過的url時,瀏覽器會在HTTP請求頭新增一個If-Modified-Since的標記,用來詢問伺服器該時間之後檔案是否被修改過。vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案

  4. Etag/If-None-Match
    a.當瀏覽器第一次請求一個url時,伺服器端的返回狀態碼為200,同時HTTP響應頭會有一個Etag,存放著伺服器端生成的一個序列值。
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案
    b.瀏覽器第二次請求上一次請求過的url時,當瀏覽器第一次請求一個url時,伺服器端的返回狀態碼為200,同時HTTP響應頭會有一個Last-Modified標記著檔案在伺服器端最後被修改的時間。
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案
  5. Etag 主要為了解決 Last-Modified 無法解決的一些問題:

    a.Etag和Last-Modified特點:

    1).它們都屬於協商快取,對內容的有效性進行驗證。
    2).Etag的值通常為檔案內容的雜湊值;而Last-Modified為最後修改的時間。
    3).Last-Modified只能精確到秒,秒之內的內容更新Etag才能檢測。
    4).檔案有時會定時重新生成相同內容,Last-Modified不能很好辨別,某些伺服器甚至不能精確的得到檔案的最後修改時間。
    5).Etag每次服務端生成都需要進行讀寫操作,而Last-Modified只需要讀取操作,Etag的消耗是更大的。

    Etag更像是Last-Modified的一種補充、完善。

  6. Expires
    Expires是RFC 2616(HTTP/1.0)協議中和網頁快取相關欄位。用來控制快取的失效日期,要注意的是,HTTP/1.0有一個功能比較弱的快取控制機制:Pragma,使用HTTP/1.0的快取將忽略Expires和Cache-Control頭。
    可以在nginx中設定 expires:-1; 如下
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案
    其它配置:
    location ~ \.(wma|wmv|asf|mp3|mmf|zip|rar|swf|flv)$ {
                   root /var/www/upload/;
                   expires max;
           }複製程式碼

    expires 指令可以控制 HTTP 應答中的“ Expires ”和“ Cache-Control ”的頭標(起到控制頁面快取的作用)
    語法:expires [time|epoch|max|pff]
    預設值:off
    expires指令控制HTTP應答中的“Expires”和“Cache-Control”Header頭部資訊,啟動控制頁面快取的作用
    time:可以使用正數或負數。“Expires”頭標的值將通過當前系統時間加上設定time值來設定。
    time值還控制"Cache-Control"的值:
    負數表示no-cache
    正數或零表示max-age=time
    
    epoch:指定“Expires”的值為 1 January,1970,00:00:01 GMT
    max:指定“Expires”的值為31 December2037 23:59:59GMT,"Cache-Control"的值為10年。
    -1:指定“Expires”的值為當前伺服器時間-1s,即永遠過期。
    off:不修改“Expires”和"Cache-Control"的值
    
    expires使用了特定的時間,並且要求伺服器和客戶端的是中嚴格同步。
    而Cache-Control是用max-age指令指定元件被快取多久。
    對於不支援http1.1的瀏覽器,還是需要expires來控制。所以最好能指定兩個響應頭。
    但HTTP規範規定max-age指令將重寫expires頭。複製程式碼
  7. 在設定Expires、Etag/If-None-Match、Last-Modified/If-Modified-Since快取機制下,瀏覽器第一次請求和第二次請求快取機制
    a.第一次請求
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案
    b.第二次請求
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案

  8. 使用者操作與快取
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案
  9. 基於vue-cli3.x腳手架下打包的spa專案快取機制方案
    a.思路
    spa專案的網站域名對映的ip地址下入口檔案是index.html,除此之外,js、css、img、font等等靜態檔案都是從index.html下載入出來的。
    在vue-cli3.x腳手架打包下的js、css、img、font等靜態檔名都是包含hash,所以每次打包index.html載入出來的檔案都不會出現相同名稱檔案,因此也不會出現快取問題。
    因此,我們只需要通過配置nginx,設定協商快取機制,每次發版本後,讓瀏覽器每次使用的index.html,這樣js、css、img、font等靜態檔案都是最新。
    本質上,我只需要設定index.html受nginx配置的快取機制的影響就好了,js、css、img、font等靜態檔案可以留給瀏覽器自身的快取機制來控制,當不是第一次請求js、css、img、font等靜態檔案時,這些資原始檔可以from disk cache/from memory cache ,直接從快取中取對應的檔案,這樣大大減少伺服器的資源消耗,同時,通過網友檔案的載入速度和頁面渲染速度。

    b.遇到的問題
    在nginx配置過程,我們嘗試取捕捉index.html檔案,捕捉到之後設定如下:

    location ^~ /aa/ {  
            proxy_set_header  X-Real-IP  $remote_addr;
            #html 檔案不快取(你也可以設定為協商快取,但是參考其他大廠方案,一般index.html入口檔案都不作快取,一方面是因為index.html很小基本在1KB左右,另外這個檔案內容變化頻繁)
            if ($request_filename ~* ^.*?.(html|htm)$){
                expires -1s;
                add_header Cache-Control no-cache;
               }
             proxy_pass  http://XXX/aa/;               
          } 複製程式碼

    但是這樣是捕捉不到index.html的,無法控制index.html的快取。
    原因分析,是因為我們在捕捉過程沒有一個檔案路徑是含有.html字尾名的,因此無法過濾出index.html加以設定。

    c.解決方案
    我們無法通過捕捉含有.html後面的路徑,但是我們知道“/aa/”所對應的檔案就是index.html,所以我們來捕捉這個“/aa/”路徑,且不捕捉該路徑下子級檔案(這樣把子資料夾中的js、css、img、font等靜態檔案也設定來),配置如下:

    location ^~ /aa/ {  
       proxy_set_header  X-Real-IP  $remote_addr;        #html 檔案不快取(你也可以設定為協商快取,但是參考其他大廠方案,一般index.html入口檔案都不作快取,一方面是因為index.html很小基本在1KB左右,另外這個檔案內容變化頻繁)
            if ( $request_uri = "/aa/") {
                expires -1s;
                add_header Cache-Control no-cache;
           #add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";           }
             proxy_pass  http://XXX/aa/;           
          } 複製程式碼
    如此,我們就能簡單的過濾出index.html,並加以設定快取。
    設定完成後瀏覽器第一次載入:
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案

    設定完成後瀏覽器第二次載入:
    vue SPA專案,瀏覽器和nginx反向代理快取問題解決實方案

    通過以上設定,我們可以很輕鬆的解決了瀏覽器和伺服器之間的快取問題。

    有建議或問題可以加群qq交流535798405


相關文章