Next.js14中使用<Link>跳转自身页面时Loading.tsx不生效
问题
使用Next.js 14.2.7,根页面为服务器组件,通过Server Action获取票据,代码如下:
import { random } from "lodash"; import Link from "next/link"; const getTicket = () => new Promise<number>((r) => { const ret = random(1, 10000); setTimeout(() => r(ret), 2000) }) export default async function Home() { const newTicket = await getTicket(); // Get a new ticket from server action return ( <> <h1>Your new ticket is: {newTicket}</h1> <Link href={`/?q=${newTicket}`}>Get a new ticket</Link> </> ); }
Loading页面代码:
export default async function Loading() { return ( <h1 className="loader"> LOADING... </h1> ) }
点击浏览器刷新按钮时Loading页面可正常显示,但点击“Get a new ticket”链接时,页面会阻塞2秒直至新票据生成,Loading页面不生效。请问是否误解了Loading页面的用法?此场景下有哪些特殊配置或编码注意事项?
原因分析
你确实误解了Next.js中Loading.tsx的触发逻辑:
Loading.tsx是路由级加载组件,仅在初始页面刷新或切换到全新路由的服务器组件页面时触发。- 点击
Link导航到同一个路由(仅查询参数变化)时,Next.js会复用当前页面的服务器组件实例,直接重新执行组件内的异步逻辑获取数据,不会触发路由级的Loading.tsx。
解决方法与注意事项
1. 改用Server Action触发数据更新(推荐)
将getTicket声明为Server Action,通过交互触发数据刷新,而非路由导航:
- 第一步:将
getTicket改为Server Action
// app/actions.ts 'use server'; import { random } from "lodash"; export async function getTicket() { return new Promise<number>((r) => { const ret = random(1, 10000); setTimeout(() => r(ret), 2000); }); }
- 第二步:创建客户端组件作为交互入口,调用Server Action并刷新页面
// app/components/TicketButton.tsx 'use client'; import { useRouter } from 'next/navigation'; import { getTicket } from '../actions'; export default function TicketButton() { const router = useRouter(); const handleClick = async () => { await getTicket(); // 刷新当前路由,触发服务器组件重新获取数据 router.refresh(); }; return ( <button onClick={handleClick}>Get a new ticket</button> ); }
- 第三步:在根服务器组件中引入该客户端组件
// app/page.tsx import { getTicket } from './actions'; import TicketButton from './components/TicketButton'; export default async function Home() { const newTicket = await getTicket(); return ( <> <h1>Your new ticket is: {newTicket}</h1> <TicketButton /> </> ); }
- 注意:可在客户端组件中添加本地加载状态(比如点击按钮时显示"加载中..."),进一步优化用户体验。
2. 拆分页面为客户端组件+服务器组件
如果需要保留路由导航的方式,可将页面拆分为:
- 服务器组件:负责获取并渲染票据数据
- 客户端组件:负责处理导航交互,在跳转前显示本地加载状态
3. 正确理解Loading.tsx的适用场景
Loading.tsx仅用于跨路由切换时的加载状态展示,比如从/home跳转到/dashboard这类不同路由的场景。- 同路由内的数据更新,应该通过本地加载状态或Server Action + 路由刷新的方式处理,而非依赖
Loading.tsx。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

