1. 程式人生 > >解決瀏覽器緩存導致頁面非最新的小技巧

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

ssa 需求 標識 固定 ges 方式 執行 etime 服務

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

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

為什麽普通的策略會出現頁面非最新?

通常使用上述策略能夠保證頁面最新。但是有時候會出現兩種情況導致頁面非最新:瀏覽器原因和服務端原因。

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

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

怎麽解決?

對於服務端導致的緩存問題,需要排查出現 304 的原因,針對性的解決,這裏就不再介紹了。

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

  1. 全部緩存頁面
    這種情況通常出現在 safari 後退或者內存不夠重新加載時,特點是頁面所有信息都被緩存起來,這時候想要重新請求頁面文件是沒有任何辦法的,
    只能上顯示時判斷是否需要進行頁面的刷新。由於頁面緩存後不執行 onload 事件,需要在 onpageshow 事件中判斷是否需要重新加載頁面。

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

基礎實現

最簡單的做法是提供一個 API,保存版本信息,每次加載時請求最新版本信息,
如果不是最新則提示。基本邏輯如下:

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

遇到的問題是:當前頁面的版本如何保存和怎麽變動工作量最小?

版本保存我們可以以文件的方式進行保存,通過 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 {}

當然,你可以做一些交互或者其他更復雜的操作,完全看個人需求了。

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