zoukankan      html  css  js  c++  java
  • 解决浏览器缓存导致页面非最新的小技巧

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

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

    当然,你可以做一些交互或者其他更复杂的操作,完全看个人需求了。

  • 相关阅读:
    【http】HTTP请求方法 之 OPTIONS
    【javascript基础】函数前面的一元操作符
    【javascript基础】运算符优先级
    【移动互联网开发】Zepto 使用中的一些注意点 【转】
    【jQuery】IE9 jQuery 1.9.1 报 Syntax error,unrecognized expression 错误
    一月收集几个有用的谷歌Chrome插件
    【Sizzle学习】之关于【初探 jQuery 的 Sizzle 选择器】这篇文章里的小bug
    【第三方类库】underscore.js源码---each forEach 每次迭代跟{}比较的疑惑
    vue-cli脚手架npm相关文件解读(2)webpack.prod.conf.js
    vue-cli脚手架npm相关文件解读(1)webpack.base.conf.js
  • 原文地址:https://www.cnblogs.com/dreamless/p/9597514.html
Copyright © 2011-2022 走看看