Next.js 的 loading.js 为什么有时候不生效?
我在用 Next.js 13 的 App Router,按照文档在页面文件夹里加了 loading.js,但有些路由切换的时候 Loading 组件根本不显示,是哪里没配对吗?
比如我从首页点进用户详情页,明明网络请求要等一两秒,但页面直接卡住,loading UI 完全没出来。我试过把 loading.js 放在和 page.js 同级目录,也确认导出的是默认组件:
export default function Loading() {
return <p>Loading...</p>;
}
是不是因为用了 useEffect 做数据获取?还是说某些跳转方式会绕过 Loading 机制?
第一步,先搞清楚 loading.js 的触发条件。
loading.js 本质上是 React Suspense 的语法糖,它只有在服务端组件进行异步数据获取时才会生效。Next.js 会在服务端等待数据请求完成,期间把 loading.js 的内容作为 fallback 显示给用户。
你说用了 useEffect 做数据获取,这大概率就是问题所在。useEffect 是在客户端执行的,这时候页面已经加载完了,loading.js 早就完成使命了,自然看不到任何效果。
第二步,检查你的数据获取方式。
看看你的页面组件是不是这样写的:
这种写法的问题在于,页面会先完整渲染一个空壳发给客户端,然后客户端才开始请求数据。loading.js 在服务端阶段就结束了,根本等不到你的 useEffect 执行。
第三步,改成服务端组件的方式。
把数据获取逻辑移到服务端,让 Next.js 在服务端等待数据:
这样写的话,Next.js 在服务端执行 getUser() 期间,会自动显示 loading.js 的内容。
第四步,确认你的目录结构。
目录结构要这样放才对:
loading.js 必须和对应的 page.js 放在同一个文件夹里,不是放在 page.js 的上一层。
第五步,还有一个容易被忽略的点。
如果你的页面已经被静态生成了,或者有缓存,那 loading 也不会显示,因为根本没有"等待"这个过程。可以在 fetch 里加
cache: 'no-store'或者在页面组件里加export const dynamic = 'force-dynamic'来强制动态渲染:最后总结一下,你的问题大概率就是客户端组件 + useEffect 这个组合导致的。改成服务端组件直接 async/await 获取数据,loading.js 就能正常工作了。
如果非要用客户端组件,那就得自己用 useState 管理加载状态,loading.js 帮不了你。这也是很多从 React SPA 转过来的开发者容易混淆的地方,毕竟习惯了自己管理 loading 状态。