写点什么

三步根治前端缓存“顽疾”

  • 2025-06-27
    北京
  • 本文字数:3197 字

    阅读完需:约 10 分钟

引言:你的代码更新,用户为何“视而不见”?

“我明明部署了新版本,用户为什么还在看旧页面?!” —— 这是多少前端开发者深夜加班时的灵魂拷问。


问题的根源往往不是代码没上传,也不是服务器抽风,而是浏览器和服务器联手上演的一场“缓存大戏”。


本文将带你化身“侦探🕵️♂️”,用三步精准定位问题,手把手教你解决页面未更新问题,让用户永远看到最新鲜的页面!



一、案发现场:缓存是如何“偷梁换柱”的?

1.1 经典症状

•用户反馈页面功能异常,但开发者本地测试正常


•浏览器反复刷新后,index.html 引用的仍是旧版 JS/CSS 文件


•查看网络请求,某些文件状态码显示 304 Not Modified200 OK (from memory cache)

1.2 幕后黑手



二、破案工具:缓存控制响应头

2.1 强缓存 vs 协商缓存

强缓存:浏览器直接使用本地副本,不询问服务器


Cache-Control: max-age=31536000  # 缓存一年!
复制代码


协商缓存:浏览器询问服务器资源是否变化


Last-Modified: Wed, 20 May 2024 08:00:00 GMT  ETag: "abc123"  
复制代码

2.2 核心原则

HTML 文件:禁止缓存,永远从服务器获取最新版本


静态资源(JS/CSS/图片) :带 hash 文件名 + 长期缓存


静态资源(JS/CSS/图片) :非 hash 文件名 + 短期缓存


# 构建产物示例/dist/index.html/dist/logo.png/dist/assets/main.3f7a8b.js/dist/assets/style.abcd12.css/dist/assets/hello.ac2314.png
复制代码



三、终极方案:三步根治缓存问题

3.1 第一步:Nginx 精准狙击(配置示例)

server {    listen 80;    server_name your-domain.com;    root /path/to/your/dist;
# ============================================== # 1. 处理 /assets/ 目录下的带哈希资源(长期强缓存) # ============================================== location ^~ /assets/ { # 缓存 1 年(兼容旧浏览器) expires 1y; # 现代浏览器强缓存(immutable 表示内容不可变) add_header Cache-Control "public, max-age=31536000, immutable"; # 安全加固,防止 MIME 类型嗅探 add_header X-Content-Type-Options "nosniff"; # 直接返回文件,不再检查其他规则 try_files $uri =404; }
# ============================================== # 2. 处理其他目录下的无哈希静态资源(短期缓存) # ============================================== location ~* .(?:js|mjs|css|png|jpg|jpeg|gif|ico|svg|webp|avif|woff|woff2|ttf|otf|eot|mp4|mp3|webm|ogg|json|xml|txt|csv|wasm)$ { # 排除 /assets/ 目录的干扰 if ($request_uri ~* "^/assets/") { break; # 跳过此规则 }
# 不包含哈希值的静态资源,设置较短的缓存时间 add_header Cache-Control "public, max-age=3600, must-revalidate"; # 安全加固,防止 MIME 类型嗅探 add_header X-Content-Type-Options "nosniff"; # 直接返回文件,不再检查其他规则 try_files $uri =404; }
# ============================================== # 3. 处理前端路由请求(禁用缓存) # ============================================== location / { # 禁用缓存 add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Pragma no-cache; add_header Expires 0; # 先尝试匹配静态资源,否则回退到 index.html try_files $uri $uri/ @fallback; }
# ============================================== # 4. 统一回退到 index.html # ============================================== location @fallback { rewrite ^ /index.html last; }}
复制代码


关键解释


immutable:告诉浏览器“此文件永不变”,跳过协商缓存


no-store:对 HTML 文件下达“禁用缓存”绝杀令


must-revalidate:资源过期时,在向原始服务器验证之前,缓存不能用该资源响应后续请求


Pragma no-cache:禁用缓存



3.2 第二步:构建 hash 文件名(自动防旧)

使用构建工具在构建时为文件名添加 hash


# 构建产物示例/dist/assets/main.3f7a8b.js/dist/assets/style.abcd12.css
复制代码


文件内容变化 → hash 值变化 → 静态资源****缓存自动失效



3.3 第三步:版本号核弹(兜底方案)

检查版本更新 1.构建时通过插件 @jd/plugin-create-version 在 HTML 中埋入版本时间、生成 version.json 2.调接口切换路由时进行检查并提示刷新



index.html


<!-- 注入构建时间戳 --><meta name="version-time" content="2025-03-04 06:57:50">
复制代码


version.json


{  "versionTime": "2025-03-04 06:57:50"}
复制代码


checkVersion.ts(简略代码,完整版请咚咚)


/** * 检查系统版本更新 * 获取最新版本并与本地版本对比 * 若版本不同,显示更新提醒message */export const checkVersion = async () => {  // 获取本地 meta 标签中的版本号  let localVersion: string | null = '';  const metaTag = document.querySelector('meta[name="version-time"]');  if (metaTag) {    localVersion = metaTag.getAttribute('content');  }
// 获取远程最新版本 const response = await fetch(`/version.json?t=${Date.now()}`, { cache: 'no-store', }); const { versionTime: latestVersion } = await response.json();
// 版本对比 if (localVersion && latestVersion && localVersion !== latestVersion) { ElMessage('发现新版本,点击刷新获得更加体验~'); }};
复制代码



四、破案验证:你的配置真的生效了吗?

4.1 一键验证工具

# 查看 路由/HTML 响应头(应禁用缓存)curl -I https://your-domain.com/about
# 期望输出:HTTP/1.1 200 OKCache-Control: no-store, no-cache, must-revalidateExpires: 0
# ==============================================
# 查看 带hash静态资源 文件响应头(应长期缓存)curl -I https://your-domain.com/assets/main.3f7a8b.js
# 期望输出:HTTP/1.1 200 OKCache-Control: public, max-age=31536000, immutableExpires: Wed, 20 May 2025 00:00:00 GMT
# ==============================================
# 查看 不带hash静态资源 文件响应头(应短期缓存)curl -I https://your-domain.com/logo.png
# 期望输出:HTTP/1.1 200 OKCache-Control: public, max-age=3600, must-revalidate
复制代码

4.2 经典翻车现场

症状:配置改完缓存依旧


排查清单


◦是否忘记 nginx -s reload


所有分组都要 nginx -s reload,即便使用的 nginx 配置集合


/assets/ 目录与项目 hash 文件静态资源目录名不一致


▪hash 文件静态资源目录为 /static/ ?


▪匹配多个静态资源目录 ~* ^/(css|js|img)/


◦是否被 CDN 缓存背刺?


Service Worker 是否在搞鬼?



五、高级技巧:当缓存遇上跨域和接口代理

5.1 接口代理的缓存隔离

location /api {    proxy_pass http://backend-server/api;
# 接口专属配置:禁用缓存 + 跨域控制 add_header Cache-Control "no-store"; add_header Access-Control-Allow-Origin "https://your-frontend-domain.com"; add_header Access-Control-Allow-Credentials "true";}
复制代码

5.2 安全加固(防御 MIME 嗅探攻击)

add_header X-Content-Type-Options "nosniff";  # 禁止浏览器猜测文件类型
复制代码



六、总结:缓存是把双刃剑

用得好:提升性能,降低服务器压力


用不好:用户看不到新功能


永远对缓存保持敬畏! 新版本部署时使用灰度发布、查看监控报警

四条黄金法则

1.HTML 永远禁用缓存


2.带 hash 静态资源 + 长期缓存


3.不带 hash 静态资源 + 短期缓存


4.部署后第一时间验证响应头

发布于: 刚刚阅读数: 2
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
三步根治前端缓存“顽疾”_京东科技开发者_InfoQ写作社区