Cloudflare Rocket Loader 对 Astro GitHub 卡片初始化的影响与排查
问题现象
我站点里的 GitHub 卡片来自一段 Markdown 指令:
::github{repo="Jeric-X/SyncClipboard"}本地开发环境和本地生产预览里,这个卡片都能正常显示;但部署到 Cloudflare 之后,却出现了一个很怪的问题:
- 直接刷新文章页时,卡片可以正常显示。
- 从站内其他页面跳转进入文章页时,卡片会一直停留在加载状态。
- 打开无痕窗口依然可以稳定复现。
- 浏览器的 Network 面板里,看不到对应的 GitHub API 请求。
这说明问题并不是单纯的网络错误,更像是前端脚本根本没有在首次切页时执行。
最开始怀疑过的方向
一开始比较容易怀疑这几个方向:
- GitHub API 被限流或被 Cloudflare 拦截。
- Cloudflare Pages 构建产物没有更新。
- Cloudflare 的缓存把旧页面缓存住了。
- 文章页里的脚本在切页后执行时机不对。
如果只是看表象,很容易把问题归咎到 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 卡片的旧实现又恰好是:
- Markdown 被渲染成一张卡片。
- 卡片内部再塞一段
fetch(...)的内联脚本。 - 依赖这段脚本在正确时机执行,去拉 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 的功能开关,而不是先怀疑构建和接口。