如何强制客户端刷新缓存

本文探讨了客户端缓存带来的挑战,特别是脚本更新时的问题。介绍了通过使用版本号和ETag来解决缓存不更新的方法,并提供了一种利用XMLHttpRequest发送GET请求来强制更新缓存的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

客户端缓存脚本通常让我们又爱又恨,爱他,是因为他确实可以有效防止相同的文件在客户端和服务器之间传来传去,恨他,是因为当你真的需要更新他的时候,他可能不理会你的要求。

以至于很多人直接在脚本后面加一个时间戳作为参数,当我们每次去获取网页的时候,都会在后面去增加一个时间戳,这样脚本文件就会每次都回传给浏览器,具体表现为你每次F5刷新页面(不是CTRL+F5)的时候,返回状态码始终都是200。

当然,这么残暴地写,在一些企业内网的环境,也没什么大不了,但是还是很多人会有点儿洁癖。其实只要在每次变更的时候强制大家刷新一下,不是就可以了吗?于是大家认为在后面加一个版本号,如v=2之类的,这样只需要每次更新脚本后,在引用的页面,更新这个v,就可以让客户端更新脚本了。

image

在这里,推荐大家用IE的F12开发者工具来抓网络包,当然Fiddler等也都可以,不过确实在这件事上,一个是没必要,一个可能Fiddler在修改IE端口指向8000之后可能会有问题。

回到话题,这里需要解释一下浏览器对客户端缓存看的是整条URL包括后面的参数,所以当你有个地方引用:

http://volnet.cnblogs.com/scripts/demo.js 的时候,你再次访问

http://volnet.cnblogs.com/scripts/demo.js?v=2 的时候,该缓存仍然在你的浏览器里呆着,当你下次继续访问不带参数的demo.js的时候,你引用的仍然还是旧的文件。

你可能对此表示不以为然,因为大部分的脚本通常都是你自己在引用,因此你更新他们的时候,总是很容易。但是有的时候,你的脚本会被第三方引用,当你变更脚本的时候,你希望他们尽量少去改动,这时候,你可能就会遇到客户端无法刷新脚本的问题。

这时候你可能会想到Last-Modified和ETag标签,这些标签的出现,既可以替代在js文件后面加版本号或者时间戳的问题,在服务器修改他们后,通常可以被监测到,并修改服务器的ETag值,当下次从客户浏览器传回If-None-Match和If-Modified-Since的时候就可以在服务器判断是应该返回304呢,还是返回200呢?看上去挺美好的事情,经常会有各种各样的意想不到。谁知道这些看上去很简单的东西,并不是每个浏览器都具备的能力,而且可能的原因还来自各种各样千奇百怪的客户端设置,抛开他们的问题而言。摆在眼前的事实就是,客户端的脚本就是因为304而不更新了,你能怎么办?你抓回来的包,可以证明你的服务器下发了ETag,但人家就是不给你返回If-None-Match,你也不太可能在服务器去修改脚本的版本号,难道你要让大家都按Ctrl+F5么?(可怜的事情还真不是发生在一些过时的浏览器上,今天找到的几个问题,IE9/IE8全都遇上了)。

这里有个问题需要说一下,就是当你的文件已经是304的时候,除非服务器支持ETag并且你的客户端带回If-None-Match标记,或者是Last-Modified和If-Modifed-Since组合的时候,原来那个链接,基本上都不会给你返回200,这或许是你早期的服务器设置所造成的,他不会因为你重新下发ETag,而让他们去给你返回这些值,除非你这次是200,并同时下发了那些用来缓存的标记。这个结论是我推导出来的,可能是IE9的行为bug,或者时设计使然。

我们如何避免他们呢?

我想来想去,既然服务器在我手里,我可以控制,并且它确实下发了ETag,那么我何不就借服务器的能力,让它把状态200发下去呢?激发它下发状态200,就是让它回传一个不一样的ETag值(在客户端叫If-None-Match),这样服务器自然就会下发了,而那些一直缓存不更新的客户,通常是因为没有带任何与之相关的参数,而宣布刷新资源失败。这里我用了XMLHttpRequest去发一个GET请求,并把驱动状态码200的必要条件给带上,就可以了。

var httpCacheUtil = {
            createXHR: function () {
                if (typeof XMLHttpRequest != "undefined") {
                    return new XMLHttpRequest();
                }
                else if (typeof ActiveXObject != "undefined") {
                    if (typeof arguments.callee.activeXString != "string") {
                        var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"];
                        for (var i = 0, len = versions.length; i < len; ++i) {
                            try {
                                var xhr = new ActiveXObject(versions[i]);
                                arguments.callee.activeXString = versions[i];
                                return xhr;
                            }
                            catch (ex) {
                                // pass
                            }
                        }
                    }
                    return new ActiveXObject(arguments.callee.activeXString);
                }
                else {
                    throw new Error("No XHR object available");
                }
            },
            update: function(url){
                try {
                    var success = function(responseText) {
                    
                    };
                    var error = function(errorStatus) {
                    
                    };
                    var xhr = httpCacheUtil.createXHR();
                    if(typeof xhr != "undefined" && xhr != null) {
                        xhr.onreadystatechange = function (event) {
                            if (xhr.readyState == 4) {
                                if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
                                    if (typeof success === "function")
                                        success(xhr.responseText);
                                }
                                else {
                                    if (typeof error === "function")
                                        error(xhr.status);
                                }
                            }
                        };
                        xhr.open("GET", url, false);
                        xhr.setRequestHeader("If-None-Match","\"22426f327b8cd1:0\"");
                        xhr.setRequestHeader("If-Modified-Since", "Sat, 31 Dec 2011 02:51:00 GMT");
                        xhr.send(null);
                    }
                }
                catch(e){
                    // throw no exception
                }
            }
        };
        httpCacheUtil.update("http://volnet.cnblogs.com/Scripts/demo1.js");
        httpCacheUtil.update("http://volnet.cnblogs.com/Scripts/demo2.js");

那些关于ETag和Last-Modifed,Cache-Control:no-cache等的说明文档,在网上已经很多了,大家可以参考相关资料来了解浏览器缓存的相关知识。

### Vue.js 强制浏览器刷新缓存的数据更新策略 #### Nginx 配置方式 为了防止 HTML 文件被缓存,在 Nginx 中可以针对特定路径设置 `Cache-Control` 头。通过配置如下所示的指令,能够确保每次访问 `/index.html` 时都不会使用本地缓存而是向服务器发起新的请求: ```nginx location = /index.html { add_header Cache-Control "no-cache, no-store"; } ``` 此配置使得当用户加载网页或刷新页面时,即使资源未更改,也会触发对服务器的新一轮查询[^1]。 #### Meta 标签控制缓存行为 另一种方法是在项目的 `<head>` 区域加入几个特殊的 meta 标签来指示浏览器如何处理当前文档及其关联资源的缓存机制: ```html <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> <meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Expires" content="0"> ``` 这些标签的作用范围仅限于该页面本身以及其直接引用到的静态资源(如 CSS 和 JavaScript),它们告诉浏览器不要存储任何有关本页的信息,并且每当需要显示这个页面的时候都要去询问服务器是否有新版本可用[^2]。 #### 动态文件名管理 对于那些频繁变动的前端资产(比如 JS 或者 CSS 文件),可以在构建过程中为其附加唯一标识符——通常是基于文件内容计算得出的时间戳或者是哈希值。这样做的好处在于只要源码有所修改,生成出来的文件名就会随之改变,从而迫使客户端重新下载最新的副本而不是依赖旧有的缓存副本。 例如,在 Webpack 构建工具里可以通过调整输出选项实现这一点: ```javascript module.exports = { output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].chunk.js' } }; ``` 上述配置会在打包后的文件名中嵌入一个由文件内容决定的独特 hash 字符串,一旦文件发生变化,则对应的 URL 地址也将不同,因此不会受到之前缓存的影响[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值