解决浏览器缓存致使页面非最新的小技巧

解决浏览器缓存致使页面非最新的小技巧

为了保证页面访问性能最佳,咱们一般在服务端会设置缓存策略,好比说带有 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 {}

固然,你能够作一些交互或者其余更复杂的操做,彻底看我的需求了。

相关文章
相关标签/搜索