网站修复记录:首页书法链接 404 与返回后卡住
首页书法日课先跳到 404,返回后又停在入口 H。两个连续现象其实来自不同层:开发路由清单失效,以及首页入口状态没有响应浏览器历史恢复。
这次遇到的问题有一条很完整的操作路径:
- 在首页点击“书法日课”中的练习记录。
- 详情页显示 404。
- 从 404 返回首页。
- 页面停在居中的哥特体
H和We Are the World,正文一直没有出现。
两个现象紧挨着发生,看起来像同一个路由错误。实际排查后,它们来自两个不同层面。
先确认链接是否真的写错了
首页没有手写某一个作品地址。书法数据先经过 calligraphyWorkHref(),再交给本地化 Link 补上语言前缀。浏览器里实际生成的地址是:
/zh/calligraphy/zjes-practice-050这个 slug 能在书法 YAML 和 Velite 输出中找到,动态详情路由 src/app/[locale]/calligraphy/[slug]/page.tsx 也存在。继续检查后发现,不只是书法,博客和摄影的动态详情页当时也全部返回 404。
问题最终落在正在运行的 Next.js 开发进程。它的开发路由清单已经陈旧,只登记了首页和 404,没有识别后来加入的动态详情路由。重启开发服务后,Velite 内容和 Turbopack 路由清单重新生成,书法详情恢复正常。生产构建也明确列出了:
/[locale]/calligraphy/[slug]所以这里没有为了“修好”而改写正确的链接。真正需要恢复的是开发服务的路由状态。
为什么返回后会停在入口 H
第二个问题在 HomeEntrance.tsx。
首页入口有四个阶段:
type IntroPhase = "checking" | "intro" | "exiting" | "done";正常播放结束后,模块内的 introCompletedInRuntime 会记录当前运行期间已经完成入口。站内跳转回来时,首页可以直接显示,不必重复播放。
但浏览器从 404 使用历史记录返回时,可能恢复一份缓存的首页标记。组件画面回到了 checking,而原来的 effect 不一定重新执行。结果是入口遮罩仍在,首页内容继续保持:
opacity: 0;
pointer-events: none;运行时变量虽然存在,却不是一个 React 可以订阅的状态。它无法主动通知恢复后的页面重新判断。
把运行时标记变成可订阅快照
修复没有把跳过条件重新放进 sessionStorage。那样会让普通刷新也跳过入口,改变首访动画原本的行为。
现在使用 useSyncExternalStore 暴露当前运行时快照:
function useRuntimeIntroCompleted() {
return useSyncExternalStore(
subscribeRuntimeIntroCompletion,
getRuntimeIntroCompletionSnapshot,
getRuntimeIntroCompletionServerSnapshot,
);
}订阅函数同时监听 pageshow。这个事件会在浏览器恢复历史页面时触发:
function subscribeRuntimeIntroCompletion(onStoreChange: () => void) {
introCompletionListeners.add(onStoreChange);
window.addEventListener("pageshow", onStoreChange);
return () => {
introCompletionListeners.delete(onStoreChange);
window.removeEventListener("pageshow", onStoreChange);
};
}入口完成状态还会镜像成当前文档上的 data-home-intro-complete 属性。浏览器恢复同一份文档时可以立即读到它;真正刷新页面后,新文档不再带这个属性,因此入口仍会照常播放。
渲染层也不再只相信组件内部的旧 state:
const homeIsReady = homeReady || introCanSkipOnMount;
const introIsDone = introPhase === "done" || introCanSkipOnMount;只要运行时快照表明入口已经结束,正文就会恢复可见和可点击,遮罩也不会继续占住页面。
验证这条完整路径
这次没有只验证单个函数。最终检查的是用户真正会走的路径:
- 打开中文首页,等待首访入口正常结束。
- 点击
/zh/calligraphy/zjes-practice-050。 - 确认详情标题和作品内容出现,而不是 404。
- 使用浏览器返回。
- 确认首页书法区存在,入口遮罩数量为 0。
此外运行了 pnpm lint、完整测试和 pnpm build。170 项测试通过,生产构建生成了全部博客、摄影和书法动态路由。
小结
这次最容易走错的方向,是看到 404 就立即改链接,看到遮罩就直接删动画。
更准确的处理方式是把连续出现的现象拆开:链接正确,但开发路由状态已经失效;入口动画也可以保留,但完成状态必须能响应浏览器历史恢复。这样修复以后,首访体验没有被取消,正常刷新不会被跳过,返回首页也不会再被一张已经结束的门帘挡住。

