解決瀏覽器緩存致使頁面非最新的小技巧

解決瀏覽器緩存致使頁面非最新的小技巧

爲了保證頁面訪問性能最佳,咱們一般在服務端會設置緩存策略,好比說帶有 hash 類型的文件會設置過時時間爲永久,
非 hash 文件好比 html 等其餘文件設置了通用的緩存策略,即:根據 etag 或者 last-modified 來判斷文件是否更改,
而後返回 304 代碼告知瀏覽器不用下載,從而保證頁面最新。這些策略在頁面加載性能和版本維持最新之間保持了平衡。javascript

爲何普通的策略會出現頁面非最新?

一般使用上述策略可以保證頁面最新。可是有時候會出現兩種狀況致使頁面非最新:瀏覽器緣由和服務端緣由。html

  1. 使用 etag 或者 last-modified 存在的問題
    etag 是提供文件指紋的標識,這個標識的實現能夠經過多種方式,方式的實現關係着文件是否最新。一般狀況下 etag 出現沒法刷新的狀況比較少,
    出現問題可能是因服務器文件系統或者靜態文件服務出現了問題。另外一種方式是使用 last-modified(部分etag 也可能使用了時間戳)。last-modified
    的精度是精確到秒,對於服務器文件來講這遠遠不夠,1 秒內文件變更可能超過一次,或者出現文件變更但時間戳沒有變化的狀況,
    致使不論客戶端怎麼刷新都是返回 304 代碼,頁面沒法更新到最新前端

  2. 瀏覽器自身緩存
    若是是遵照 http 規範,保證每次頁面加載時都使用服務端內容,那麼就不存在緩存問題。但實際上瀏覽器爲了提升性能,
    總會進行緩存。好比說 safari 下頁面會整個被緩存起來,chrome 在輸入已有網址時會優化從 dist cache 中獲取文件,
    而不會去請求服務器。java

怎麼解決?

對於服務端致使的緩存問題,須要排查出現 304 的緣由,針對性的解決,這裏就再也不介紹了。chrome

對於瀏覽器的問題,有兩種:頁面所有緩存和只是緩存文件。json

  1. 所有緩存頁面
    這種狀況一般出如今 safari 後退或者內存不夠從新加載時,特色是頁面全部信息都被緩存起來,這時候想要從新請求頁面文件是沒有任何辦法的,
    只能上顯示時判斷是否須要進行頁面的刷新。因爲頁面緩存後不執行 onload 事件,須要在 onpageshow 事件中判斷是否須要從新加載頁面。瀏覽器

  2. 文件被緩存
    前端頁面緩存的判斷是包含了文件是否爲最新的判斷。緩存

基礎實現

最簡單的作法是提供一個 API,保存版本信息,每次加載時請求最新版本信息,
若是不是最新則提示。基本邏輯以下:服務器

fetch('/page/version').then((res) => res.json())
  .then((res) => {
    if (res && res.version && res.version !== 當前的版本) {
      提示或者刷新
    }
  })

遇到的問題是:當前頁面的版本如何保存和怎麼變更工做量最小?antd

版本保存咱們能夠以文件的方式進行保存,經過 import 後,固定在代碼中。這樣就能夠實現基本的工做。

簡化實現

基礎實現存在的問題是:版本須要提供獨立的 API,對於前端來講配合過程太麻煩。那麼咱們就考慮下版本信息放在文件中。
放在文件中首先要解決的一個問題是:文件會被緩存,並且比較嚴重。解決這個問題就在 url 中添加時間戳,保證每次 URL 不一樣,

最終作法以下:

  • version 文件存放在 static 或者 public 文件下,這樣在打包後會將該文件複製到文件下。
  • 當前版本保存:經過js import 進文件,導入變量
  • 獲取服務端版本:請求靜態文件(添加時間戳保證不被緩存
  • 對比:服務端版本存在且不等於當前版本,提供通知,讓用戶手動刷新,或者直接使用 window.location.reload(true)

代碼以下(使用 antd 中的 notication 進行通知,其餘類型的通知也相似):

import { notification } from 'antd'
import page from '../../public/pageVersion.json'

fetch(`/pageVersion.json?_=${Date.now()}`).then(res => res.json())
  .then((res) => {
    if (res.version && page.version !== res.version) {
      notification.open({
        message: '頁面過時',
        description: `當前頁面已通過期,最近更新時間爲${res.updateTime},請手動刷新瀏覽器頁面以便獲取更好體驗`,
        duration: null
      })
    }
  })

export default {}

固然,你能夠作一些交互或者其餘更復雜的操做,徹底看我的需求了。

相關文章
相關標籤/搜索