手摸手教你解决重定向缓存问题

3,821 阅读3分钟

手摸手教你解决重定向缓存问题

问题产生:

测试粑粑:测试环境已经搭好了,前端打个包吧!
weber:好嘞!快速输入命令:npm run build:stagging
三分钟后……
测试粑粑:是不是打错包了?怎么还没变?
weber:刷新下页面试试!
测试粑粑:没用鸭~
weber:蛤~清理下浏览器缓存!
测试粑粑:出来了,**牛皮鸭!!!

问题到这里,我陷入了沉思,咋回事呢,如果每次发布需要用户手动清除浏览器缓存,这无疑是一个巨大的bug?于是开始列出了问题排查清单:
1、刷新页面重新进行了doc【index.html】请求,无效;而清理浏览器缓存缺生效了,说明浏览器缓存了文件导致刷新请求时还是之前的文件。
2、webpack打包时,outputjscss加上了hash,那jscss应该就不会缓存。

开始排查

一、检查了webpack打包配置:

module.exports = {
  context : path.join(__dirname,'src'),
  entry:{
    main: './index.js',
    vender:['./jquery.js','./test.js']
  },
  module:{
    rules:[{
      test:/\.css$/,
      use: extractTextPlugin.extract({
        fallback:'style-loader',
        use:'css-loader'
      })
    }]
  },
  output:{
    path:path.join(__dirname, '/dist/js'),
    filename: 'bundle.[name].[hash].js',
  },
  plugins:[
    new extractTextPlugin('../css/bundle.[name].[hash].css')
  ]
}

发现webpack打包没有问题。

二、打包后的文件也没有问题。

三、打包后生成的index.html文件引入。

三、查看network请求才恍然大悟:

index.html304请求,那说明index.html被缓存了。

解决问题

开始寻找解决办法,方法有很多,有前端配置的,也有后端配置的。

考虑到webpack打包生成的html只是作为引入jscss的容器,若只是不缓存html文件,代价其实很小。选择如下方法:

1、在html head头部添加不缓存html配置:

注:metatml语言head区的一个辅助性标签,其中的http-equiv字段定义了服务器和用户代理的一些行为。在之前的规范中,metahttp-equiv字段中有以下值与http header缓存相关的字段功能类似

<head>
  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0">
</head>

若采用这种方式,请在public文件加下的index.html配置。

2、修改nginx.conf,增加add_header Cache-Control 'no-cache, no-store, must-revalidate'不缓存html配置:

server {
  listen       80;
  server_name  localhost;

  root        /usr/share/nginx/html;
  index       index.html;

  location ~* \.(gif|jpg|jpeg|png|css|js|ico|eot|otf|fon|font|ttf|ttc|woff|woff2)$ {
      expires 1M;
      add_header Cache-Control "public";
  }

  location / {
      add_header Cache-Control 'no-cache, no-store, must-revalidate';
  }

  error_page  404              /index.html;
}

其实这两个配置都是在请求head加上不缓存html的配置,一个在前端,一个在运维。翻阅w3c文档发现,但现在w3c的规范字段中这些值已经被移除,一个很好的理由是:

Putting caching instructions into meta tags is not a good idea, because although browsers may read them, proxies won't. For that reason, they are invalid and you should send caching instructions as real HTTP headers.

综合之后还是第二种方案较为完善。

bug再现:

weber:这次修改了nginx配置,这次打包应该不会出现上次的缓存问题了,不用手动清楚浏览器缓存了!
测试粑粑:牛皮鸭,我试试~
weber:(自信的小表情,期待的小红手)~
测试粑粑:还是有问题鸭!还是没更新过来!
weber:你是直接请求是吧?有没有刷新页面?
测试粑粑:没有,直接请求的~
weber:好了,我知道了!

问题到这里,又出现了bug。如果直接请求网址,是会请求到最新的html文件,但是还会存在用户不刷新直接请求的情况。

理清思路:

其实到这一步问题已经很清晰了,重新理一遍webpack打包过程、浏览器缓存策略,问题就很容易解决了。
一、webpack打包:

public/index.html:文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。另外,Vue CLI 也会自动注入 resource hint (preload/prefetch、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScriptCSS 文件的资源链接。ouput里面的文件都会inject

二、HTTP重定向:

服务器的nginx配置如下:

假若输入:https://example.comhttps://example.com/a都会定位到root下的index.html文件,也会触发请求html文件。

三、浏览器缓存:

再次查看增加了不缓存html文件,请求状态变成了200

因此,在不刷新浏览器的情况下请求,html是没有被请求的,仍然是第一次输入网址请求回来的html文件,里面的jscss也是之前的,所以浏览器不会再去请求jscss文件。解决办法:

axios请求加上版本号,如果版本号对应不上,则返回code,让前端去与用户交互是否已经更新了,需要刷新页面。