Cloudflare Rocket Loader 对 Astro GitHub 卡片初始化的影响与排查

1642 字
8 分钟
Cloudflare Rocket Loader 对 Astro GitHub 卡片初始化的影响与排查

问题现象#

我站点里的 GitHub 卡片来自一段 Markdown 指令:

::github{repo="Jeric-X/SyncClipboard"}

本地开发环境和本地生产预览里,这个卡片都能正常显示;但部署到 Cloudflare 之后,却出现了一个很怪的问题:

  • 直接刷新文章页时,卡片可以正常显示。
  • 从站内其他页面跳转进入文章页时,卡片会一直停留在加载状态。
  • 打开无痕窗口依然可以稳定复现。
  • 浏览器的 Network 面板里,看不到对应的 GitHub API 请求。

这说明问题并不是单纯的网络错误,更像是前端脚本根本没有在首次切页时执行。

最开始怀疑过的方向#

一开始比较容易怀疑这几个方向:

  1. GitHub API 被限流或被 Cloudflare 拦截。
  2. Cloudflare Pages 构建产物没有更新。
  3. Cloudflare 的缓存把旧页面缓存住了。
  4. 文章页里的脚本在切页后执行时机不对。

如果只是看表象,很容易把问题归咎到 GitHub API 或 Cloudflare 平台本身,但真正有用的不是猜,而是逐层排除。

关键排查过程#

1. 先看 Network 面板#

首次通过站内导航进入文章页时,Network 面板里根本没有发出下面这个请求:

https://api.github.com/repos/Jeric-X/SyncClipboard

这一步几乎就能说明:

不是请求失败,而是请求压根没有发出去。

也就是说,真正的问题在于 GitHub 卡片的初始化逻辑没有跑到,而不是 API 被拦了。

2. 再看远端页面里的实际 HTML#

查看远端文章页里 GitHub 卡片对应的 HTML 后,发现它仍然是旧实现:

<a id="GCvwqhpb-card" class="card-github no-styling" href="https://github.com/Jeric-X/SyncClipboard" target="_blank" repo="Jeric-X/SyncClipboard">
...
<script id="GCvwqhpb-script" type="text/javascript" defer="">
fetch('https://api.github.com/repos/Jeric-X/SyncClipboard', { referrerPolicy: "no-referrer" })
.then(response => response.json())
.then(data => {
...
})
</script>
</a>

这里有两个关键信号:

  • 卡片节点带的是随机 id,例如 GCvwqhpb-card
  • 每张卡片内部都内联了一段 fetch(...) 脚本。

这意味着卡片依赖的是“每张卡片自己带一段脚本,在页面解析时执行”的方案。

真正有用的一步:对比原始域名和自定义域名#

接下来分别检查了两个入口:

  • Cloudflare Pages 提供的原始 *.pages.dev 域名
  • 自己绑定的自定义域名

结果非常关键:

  • pages.dev 域名下文章页正常。
  • 自定义域名下文章页异常。

这一步直接把问题范围缩小到了:

不是 Astro 构建本身有问题,而是自定义域名这一层额外加的 Cloudflare 功能影响了脚本执行。

清构建缓存和重新部署都没用#

为了排除“旧产物没更新”的可能,我还试过清空构建缓存并重新部署,但问题依旧存在。

这说明问题并不在于 Pages 没有拉到最新代码,而在于:

  • 自定义域名链路上还有额外处理。
  • 某个 Cloudflare 功能在改变脚本加载或执行时机。

根因:Rocket Loader#

最终定位下来,真正导致问题的是 Cloudflare Rocket Loader

把它关闭之后,自定义域名下的 GitHub 卡片立即恢复正常,不再需要刷新一次才显示。

WARNING

如果你的站点依赖页面内联脚本、局部切页脚本,或者对脚本初始化时机比较敏感,Rocket Loader 很容易把原本“偶发”的问题放大成“稳定复现”的问题。

为什么 Rocket Loader 会把问题放大#

我的站点启用了局部切页,文章内容不会每次都走完整的整页刷新流程。

GitHub 卡片的旧实现又恰好是:

  1. Markdown 被渲染成一张卡片。
  2. 卡片内部再塞一段 fetch(...) 的内联脚本。
  3. 依赖这段脚本在正确时机执行,去拉 GitHub API 并回填卡片内容。

这套方案本身就对执行时机比较敏感。一旦再叠加 Rocket Loader 这类会调整脚本加载策略的功能,就很容易出现下面这种情况:

站内跳转进入文章页
-> 卡片 DOM 已经插入
-> 但卡片内联脚本没有按预期执行
-> 没有发出 GitHub API 请求
-> 卡片一直停留在 loading

而刷新页面时,由于浏览器重新完整解析整页 HTML,脚本又有机会执行,所以看起来就成了“刷新一下就好了”。

这类问题怎么快速判断#

如果你也遇到“远端首次进入文章页异常,但刷新后正常”的情况,可以按这个顺序排查:

1. 先看 Network#

看首次异常时有没有发出对应的 API 请求。

  • 有请求但失败:先查接口、限流、CORS。
  • 根本没请求:优先查脚本执行时机。

2. 对比 pages.dev 和自定义域名#

如果原始域名正常、自定义域名异常,通常就不是构建产物本身的问题,而是 Cloudflare 额外功能导致的。

3. 查看页面实际返回的 HTML#

重点看它到底是:

  • 旧的“内联 script”方案;还是
  • 新的“静态骨架 + 全局初始化”方案。

只有看清楚页面里实际返回了什么,后面的判断才不会跑偏。

4. 关闭 Rocket Loader 再验证#

如果关闭 Rocket Loader 以后问题立刻消失,基本就能确认是它在干扰脚本执行时机。

最终解决方案#

我最后采用了两个层次的处理方式。

立刻可用的方案#

先在 Cloudflare 里关闭 Rocket Loader,让线上恢复正常。

更稳的长期方案#

不要再让每张 GitHub 卡片自己带一段内联脚本,而是改成:

  • Markdown 只输出静态卡片骨架。
  • 用统一的 data-* 标记描述卡片状态。
  • 在全局布局脚本里统一初始化卡片。
  • 在局部切页完成后再次执行初始化。

这样就不会再把卡片是否能显示,绑死在“内联 script 是否刚好在正确时机执行”这件事上。

总结#

这次问题表面上看像是:

  • Cloudflare 有问题
  • GitHub API 有问题
  • 构建缓存没更新

但真正的根因其实更具体:

自定义域名开启了 Rocket Loader,而 GitHub 卡片又依赖内联脚本在切页后执行,两者叠加后导致首次进入文章时卡片一直停留在加载状态。

一句话总结就是:

pages.dev 正常、自定义域名异常时,优先排查 Cloudflare 的功能开关,而不是先怀疑构建和接口。

Cloudflare Rocket Loader 对 Astro GitHub 卡片初始化的影响与排查
https://lunary.cc/posts/cloudflare-rocket-loader-对-astro-github-卡片初始化的影响与排查/
作者
鹤望兰
发布于
2026-03-15
许可协议
CC BY-NC-SA 4.0