最近在开发一个后台管理系统时,遇到个让人头疼的问题:页面刷新后,某些数据还是老的。查了半天才发现,是浏览器把Ajax请求给缓存了。明明接口返回的是新数据,前端却拿不到,问题就出在缓存上。
为什么Ajax请求会被缓存?
很多人以为Ajax默认不缓存,其实不然。GET请求在某些浏览器下(比如IE、部分版本的Chrome)会默认被缓存,尤其是当URL完全相同时。比如你发了个请求:/api/user?id=123,下次再发同样的URL,浏览器可能直接从缓存取结果,根本不会走网络。
这种情况在开发阶段可能不明显,但一旦上线,在用户端就容易出问题,尤其是数据更新频繁的场景,比如订单状态、用户积分、实时消息等。
怎么判断是不是缓存导致的?
打开浏览器开发者工具,看Network面板。如果某个请求的状态码显示的是 304 Not Modified 或者 Size 栏显示 (from memory cache) 或 (from disk cache),那基本可以确定是缓存生效了。
解决方法一:URL加时间戳或随机参数
最简单粗暴的方法,就是在请求URL后面加一个不重复的参数,比如时间戳:
$.ajax({
url: "/api/user?id=123&_t=" + new Date().getTime(),
type: "GET",
success: function(data) {
console.log(data);
}
});
这样每次请求的URL都不一样,浏览器就不会用缓存。也可以用随机数:
url: "/api/user?id=123&_r=" + Math.random()
虽然看起来有点土,但在老项目里特别管用,尤其是一些第三方库封装的请求没法改配置的时候。
解决方法二:设置请求头禁止缓存
如果你用的是原生fetch或者能控制请求头,可以在发送时加上禁用缓存的指令:
fetch("/api/user?id=123", {
method: "GET",
headers: {
"Cache-Control": "no-cache",
"Pragma": "no-cache"
}
})
注意,这只能建议浏览器不要缓存,不能百分百保证。服务器端的缓存策略优先级更高。
解决方法三:服务端设置缓存控制
更彻底的办法是在服务器响应头里明确告诉浏览器别缓存。比如后端返回时加上:
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
这样无论前端怎么发,浏览器都会重新请求。适合对数据一致性要求高的接口,比如支付结果查询、用户权限变更等。
jQuery用户注意:设置cache: false
如果你还在用jQuery的$.ajax,可以直接设置cache: false:
$.ajax({
url: "/api/user",
type: "GET",
cache: false, // 自动加时间戳
success: function(data) {
console.log(data);
}
});
jQuery内部会自动在URL后面加_={timestamp},省得你自己拼。
实际场景举例
有个客户反馈说修改了头像,刷新页面还是旧的。排查发现是因为头像地址是/api/avatar?user_id=888,而这个接口被CDN缓存了5分钟。后来我们在生成URL时加了头像更新时间戳:/api/avatar?user_id=888&v=1678901234,问题就解决了。
有时候不是技术不够,而是细节没考虑到。缓存本是为了提升性能,但用错了地方反而成了坑。