开发者社区 > 博文 > 三步根治前端缓存“顽疾”
分享
  • 打开微信扫码分享

  • 点击前往QQ分享

  • 点击前往微博分享

  • 点击复制链接

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

  • jd****
  • 2025-06-24
  • IP归属:北京
  • 49浏览

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

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

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

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


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

    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. 部署后第一时间验证响应头

    评论区互动
    你在缓存问题上踩过哪些坑?分享你的故事 💥



    文章数
    1
    阅读量
    49

    作者其他文章