“比如这篇里出现的 修复 VitePress 在 Vercel 上的内容加载延迟问题 和 年度总结,都是真实的内部引用,不是普通 Markdown 链接。”
“像 修复 VitePress 在 Vercel 上的内容加载延迟问题 这种排障记录,现在也能比较自然地挂在整个站点演进过程中,而不是孤零零躺在文章列表里。”
排查并修复博客在 Vercel 上“刷新后才显示内容”问题:原来是 rewrites 规则惹的祸。
最近发现重构后的博客在 Vercel 部署后出现了一个奇怪的问题:当直接通过 URL(无 .html 后缀)访问(例如 .../posts/my-post),页面会先显示出一个空的布局,内容区域一片空白,然后在刷新后,地址栏带上了 .html 后缀,文章内容才被加载出来。
由于我的博客在 Google 的索引也是这样没有 .html 后缀的形式,因此也会出现相同的现象,导致极其糟糕的用户体验。
为了找到根源,我首先排查了 VitePress 主题中的客户端代码,检查是否存在 location.href 或 router.push 之类的手动跳转逻辑,但一无所获。
既然客户端代码没有问题,那么问题很可能出在部署平台的配置上。我的博客托管在 Vercel,因此 vercel.json 文件成为了重点排查对象。果然,我在其中发现了问题的根源:
// vercel.json (旧的错误配置)
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}上述 rewrites 规则的含义是:“无论用户请求什么路径,都返回根目录下的 index.html 文件”。
这是一个典型的 单页应用 (SPA) 配置。但 VitePress 本质上是一个 静态站点生成器 (SSG),它为每一篇文章都预先生成了独立的 HTML 文件(例如 /posts/my-post.html)。
这个错误的配置导致了如下低效的加载流程:
/posts/my-post。index.html(一个只包含基本布局和 JS 脚本的“空壳”)。.html 后缀的链接,正常显示了。解决方案是修改 vercel.json,让其在服务器端就能智能地处理这种无后缀的 URL 请求,直接返回正确的 HTML 文件。
我将 rewrites 规则修改为:
// vercel.json (新的正确配置)
{
"rewrites": [
{
"source": "/:path((?!.*\\.).*)",
"destination": "/:path.html"
}
]
}这个新规则的原理是:
source: "/:path((?!.*\\.).*)": 匹配所有不包含 . (点) 的 URL 路径。这样它就能匹配到 /posts/my-post 这样的文章路径,同时忽略 /assets/image.png 这样的静态资源文件。destination": "/:path.html": 将匹配到的路径在服务器内部重写,为其添加 .html 后缀。这样,新的高效加载流程变为:
/posts/my-post。/posts/my-post.html。posts/my-post.html 文件。这个问题提醒我们,在使用现代前端框架时,必须确保部署环境的配置与框架自身的工作模式相匹配。将为 SSG 设计的 VitePress 错误地配置为 SPA 模式,虽然刷新后最终也能显示内容,但是会导致 Google 进入的读者看到的是几乎空白的页面,一头雾水。通过重写vercel的重定向规则,我们就能轻松修复这个问题,提供更流畅的用户体验。