Firebase托管:如何防止单页应用程序的index.html被缓存

53
我在Firebase上托管一个单页面应用程序,几乎所有的路径都会被重写为index.html。我使用基于Webpack哈希的缓存破坏技术,因此我希望始终防止index.html的缓存,但不影响任何其他文件。但我发现实现这一点非常困难。具体而言,我的文件布局如下:
/
├── index.html
├── login.html
├── js
│   ├── login.ba22ef2579d744b26c65.bundle.js
│   └── main.6d0ef60e45ae7a11063c.bundle.js
└── public
    └── favicon-16x16.ico

在阅读文档中的一句话之前,我天真地使用了"sources": "index.html"

每个定义都必须有一个源键与原始请求路径匹配,而不考虑使用glob符号的任何重写规则。

好吧,所以我需要在路径上设置一个glob,而不是简单指定我想要这些头文件的文件。由于大多数路径都重定向到index.html,因此我需要一个排除我不想放置这些标头的所有路径的glob。

供参考,我的firebase.json托管部分如下:

{
  "hosting": {
    "public": "dist",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "cleanUrls": true,
    "trailingSlash": false,
    "headers": [
      {
        "source": <<<WHAT-GOES-HERE?>>>,
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache, no-store, must-revalidate"
          },
          {
            "key": "Pragma",
            "value": "no-cache"
          },
          {
            "key": "Expires",
            "value": "0"
          }
        ]
      }
    ]
  }
}

因此,为了举例说明应该不缓存并重定向到index.html的内容:

mysite.com  
mysite.com/  
mysite.com/foo/bar/baz  
mysite.com/index.html 
< p > < em > 注意:如果最后一个被缓存,我也可以生存,因为它在实践中并不会被使用。

< p > 不应重定向到index.html且不应缓存的内容

**/*.* (ideally excluding index.html)
mysite.com/login  

我自己尝试的最接近的是**/!(login|*.*),它可以匹配上面列出的几乎所有内容,但不知何故无法匹配mysite.commysite.com/。这两个页面没有与此通配符匹配,我无法弄清楚为什么。


2
你尝试过为/index.html添加一个规则吗? - Michael Bleigh
为我而言,添加一个源全局仅为“/”的第二个部分似乎可以解决问题。但我将保留这个问题未答复,因为如果可以在单个全局/部分中完成这个操作,那将更加优雅。 - zevdg
3个回答

123

这是我正在使用的配置。逻辑是对所有静态文件(如 images、css、js 等)使用缓存。对于其他所有文件,即 "source": "/**",将缓存设置为 no-cache。因此,对于所有其他文件,例如 example.com、example.com/index.html、example.com/about-us、example.com/about-us.html,将不会应用缓存。

{
  "hosting": {
    "public": "dist",
    "headers": [
      {
        "source": "/**",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache, no-store, must-revalidate"
          }
        ]
      },
      {
        "source":
          "**/*.@(jpg|jpeg|gif|png|svg|webp|js|css|eot|otf|ttf|ttc|woff|woff2|font.css)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "max-age=604800"
          }
        ]
      }
    ],
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
  }
}

1
添加了 WebP 到我的 :) - Rusty Rob
1
@GijoVargehese 当我们谈到这个问题的时候,wasm可能也很有意义。 - Alex Suzuki
2
我发现使用这种方法会使从CDN加载index.html页面的时间增加300-400毫秒(就像CDN完全停止缓存它一样),即使是在硬刷新与硬刷新之间进行比较。有没有办法在这里兼顾两全,当文件更改时使缓存失效,始终让客户端从CDN获取最新的内容? - Brian Jordan
4
更新:回答我的问题(希望听到这是个好主意还是坏主意)- 使用Cache-Control值:max-age=0,s-maxage=604800似乎能够得到我想要的行为,即在新页面内容上进行即时客户端更新,但仍在CDN级别缓存。 - Brian Jordan
1
请注意此配置,如果您的应用程序还包括服务工作者(我们在 Firebase 组织中升级了一个 P1 问题,并发现了这一点)。您需要确保服务工作者未被缓存,并且 Firebase 托管使用“最后规则获胜”,因此您需要确保您的服务工作者规则是最后一个。 - jules testard
显示剩余9条评论

4

在配置时要小心,如果您的应用程序还包括服务工作者(我们曾在firebase组织内升级P1问题,发现了这一点)。您需要确保服务工作者没有被缓存,并且firebase hosting使用“最后规则获胜”,因此您需要确保您的服务工作者规则是最后一个。最好采用以下方式:

{
  "hosting": {
    "public": "dist",
    "headers": [
      {
        "source":
          "**/*.@(jpg|jpeg|gif|png|svg|webp|js|css|eot|otf|ttf|ttc|woff|woff2|font.css)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "max-age=604800"
          }
        ]
      },
      {
        "source": "/**",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache, no-store, must-revalidate"
          }
        ]
      },
      {
        "source": "/service-worker.js",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache, no-store, must-revalidate"
          }
        ]
      }
    ],
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
  }
}

特别是因为你的服务工作者可能会进行预缓存,所以即使你的index.html在CDN层面上没有被缓存,HTTP请求会获取一个新的副本,但由于服务工作者的预缓存,你可能仍然提供旧版的index.html。


2

我最近也遇到了这个问题,发现使用源glob /**/!(*.*) 可以很好地解决它,因为它将针对任何结尾不包含.的子目录进行匹配,这基本上意味着它只匹配路由(即//login/account/edit等)。然后我就不需要为每个特定的文件扩展名编写排除规则了。

希望这能帮助到某些人(并使您的配置更加合理)!

{
  "hosting": {
    "public": "build",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "headers": [
      {
        "source": "/**/!(*.*)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache"
          }
        ]
      },
      {
        "source": "**/*.chunk.@(js|css)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=31536000, immutable"
          }
        ]
      }
    ]
  }
}

请注意,我只会积极缓存包含.chunk.的JS和CSS文件(在OP的情况下应将其更改为.bundle.)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接