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

京东云开发者
• 阅读 2

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

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

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

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




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

1.1 经典症状

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

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

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

1.2 幕后黑手

嫌疑人 作案手法 经典台词
浏览器缓存 擅自保留旧文件副本 “这个 JS 我上周刚拿过!”
Nginx 默认配置 未正确设置缓存响应头 “我按规矩办事,怪我咯?”
CDN 缓存 全球节点同步延迟(隐藏 Boss) “急什么,等我喝完这杯茶”



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

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 OK
Cache-Control: no-store, no-cache, must-revalidate
Expires: 0

# ==============================================

# 查看 带hash静态资源 文件响应头(应长期缓存)
curl -I https://your-domain.com/assets/main.3f7a8b.js

# 期望输出:
HTTP/1.1 200 OK
Cache-Control: public, max-age=31536000, immutable
Expires: Wed, 20 May 2025 00:00:00 GMT

# ==============================================

# 查看 不带hash静态资源 文件响应头(应短期缓存)
curl -I https://your-domain.com/logo.png

# 期望输出:
HTTP/1.1 200 OK
Cache-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.部署后第一时间验证响应头

点赞
收藏
评论区
推荐文章
浅谈 HTTP 缓存与 CDN 缓存的那点事
HTTP缓存与CDN缓存一直是提升web性能的两大利器,合理的缓存配置可以降低带宽成本、减轻服务器压力、提升用户的体验。而不合理的缓存配置会导致资源界面无法及时更新,从而引发一系列的衍生问题。本文将分别将从HTTP缓存与cdn缓存的规则、流程、配置
Souleigh ✨ Souleigh ✨
4年前
前端也要懂 - 带你全面认识 Nginx
前言作为一名前端开发人员,你是不是经常碰到领导让你上服务器去修改Nginx配置,然而你会以“我是前端,这个我不会”为理由搪塞过去呢!今天就让我们一起告别这种尴尬,向“真正”的程序员迈进!!!Nginx概述Nginx是开源、高性能、高可靠的Web和反向代理服务器,而且支持热部署,几乎可以做到7\24小时不间
Stella981 Stella981
3年前
Node.js 简单学习
明白JavaScript语言,你就会用Node.js了。最常见的运行JavaScript语言的地方就是用户的浏览器,几乎所有的浏览器上都有个JavaScript引擎,这个引擎负责运行在页面中嵌入的JavaScript代码。代码是在用户的浏览器上运行的,用户那头叫前端(Frontend),服务器这头叫后端(Backend)。Node.js
Wesley13 Wesley13
3年前
NGINX 设置缓存时间
设置缓存原因一般的web项目,如果是前后不分离,采用模板生成页面的话,可以灵活控制js,css等静态文件的版本号。来避免浏览器缓存的问题但是目前是前后分离了,虽然前端发版都每次指定了版本号,但是发现有时候发版的时候,还是因为浏览器缓存导致了查看页面有时候出点小问题。因此为了避免浏览器缓存可能导致的问题,因此严谨一点NGIN
Stella981 Stella981
3年前
Kerberos无约束委派的攻击和防御
 0x00前言简介当ActiveDirectory首次与Windows2000Server一起发布时,Microsoft就提供了一种简单的机制来支持用户通过Kerberos对Web服务器进行身份验证并需要授权用户更新后端数据库服务器上的记录的方案。这通常被称为Kerberosdoublehopissue(双跃点问题),
Wesley13 Wesley13
3年前
thinkphp缓存使用
thinkphp缓存使用一、总结1、这里的缓存不是指的缓存的页面,而是cache,如果你缓存了一个数组,那么你就可以取出这个数组里面的数据进行使用,用法性质和cookie和session有点像2、缓存的数据可以在thinkphp的runtime下的cache文件夹下面找到3、用法:可以用Cache类也可以用
Wesley13 Wesley13
3年前
DNS解析过程
DNS解析过程是重点也是难点,下面我按自己的理解一步步来解释,可能有错误之处,欢迎指正。以下步骤中,将DNS缓存中存在域名对应IP则DNS解析成功,用户计算机将直接访问服务器,若DNS服务器缓存中不存在域名对应IP,则自动进入下一步。1.浏览器缓存 当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的IP地址
Wesley13 Wesley13
3年前
PHP 中 9 大缓存技术总结
1、全页面静态化缓存也就是将页面全部生成html静态页面,用户访问时直接访问的静态页面,而不会去走php服务器解析的流程。此种方式,在CMS系统中比较常见,比如dedecms;一种比较常用的实现方式是用输出缓存:Ob_start()要运行的代码$contentOb_
CDN与前端技术
CDN(ContentDeliveryNetwork)是一种广泛应用于网络加速和内容分发的技术。它通过在全球各地部署服务器节点,将静态资源如图片、CSS和JavaScript文件等缓存到离用户最近的节点上,从而提供更快速和可靠的内容交付。在前理、前端与CDN的结合以及一些最/佳实践。
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(