Next.js 14中为布局添加Loading Skeleton的实现方法问询
为Next.js用户页面布局添加骨架屏加载状态
方案一:将布局请求移至客户端组件(推荐)
这种方式能让布局和子页面的加载状态各自独立,还能避免重复请求用户信息。
1. 修改根布局文件 [id]/layout.tsx
移除服务端的getUser请求,直接把用户ID传给客户端布局组件:
import UserLayout from '@/components/screens/user/layout/UserLayout' interface ILayout { children: React.ReactNode params: {id: string} } export default function Layout({ children, params: {id}}: ILayout) { return <UserLayout userId={id}>{children}</UserLayout> }
2. 改造客户端布局组件 UserLayout.tsx
加上'use client'指令,在组件内部处理用户信息的加载逻辑,同时通过Context传递用户信息给子页面,避免重复请求:
'use client' import { createContext, useContext, useState, useEffect } from 'react' import UserLayoutSkeleton from '@/components/screens/user/layout/UserLayoutSkeleton' // 创建Context用于传递用户信息 const UserContext = createContext<null | User>(null) export const useUser = () => useContext(UserContext) interface UserLayoutProps { userId: string children: React.ReactNode } export default function UserLayout({ userId, children }: UserLayoutProps) { const [user, setUser] = useState<null | User>(null) const [loading, setLoading] = useState(true) useEffect(() => { const fetchUser = async () => { try { const res = await getUser(userId) // 确保该请求方法支持客户端调用 setUser(res) } finally { setLoading(false) } } fetchUser() }, [userId]) // 加载中时显示布局骨架屏 if (loading) { return <UserLayoutSkeleton /> } // 加载完成后渲染真实布局,并通过Context传递用户信息 return ( <UserContext.Provider value={user}> <div className="user-layout"> {/* 这里渲染你的用户布局内容,比如头像、用户名等 */} <div className="user-header"> <img src={user.avatar} alt={user.name} /> <h2>{user.name}</h2> </div> {children} </div> </UserContext.Provider> ) }
3. 优化子页面避免重复请求
以(main)/page.tsx为例,通过useUser钩子获取Context中的用户信息,不用再次调用getUser:
'use client' import { useUser } from '@/components/screens/user/layout/UserLayout' export default function Page() { const user = useUser() return <User user={user} /> }
方案二:在服务端布局中使用Suspense分割加载
如果想保留服务端渲染用户信息的逻辑,可以用Suspense包裹异步布局部分,实现布局骨架屏:
1. 修改 [id]/layout.tsx
把异步请求逻辑抽离到独立组件中,并用Suspense指定加载时的骨架屏:
import { Suspense } from 'react' import UserLayout from '@/components/screens/user/layout/UserLayout' import UserLayoutSkeleton from '@/components/screens/user/layout/UserLayoutSkeleton' interface ILayout { children: React.ReactNode params: {id: string} } // 抽离异步逻辑到独立组件 async function LoadedUserLayout({ id, children }: { id: string; children: React.ReactNode }) { const user = await getUser(id) return <UserLayout user={user}>{children}</UserLayout> } export default function Layout({ children, params: {id}}: ILayout) { return ( <Suspense fallback={<UserLayoutSkeleton />}> <LoadedUserLayout id={id}>{children}</LoadedUserLayout> </Suspense> ) }
2. 创建布局骨架屏组件 UserLayoutSkeleton
按照真实布局的结构编写骨架屏,示例如下:
export default function UserLayoutSkeleton() { return ( <div className="user-layout-skeleton"> <div className="user-header-skeleton flex items-center gap-4 p-4"> <div className="avatar-skeleton w-12 h-12 rounded-full bg-gray-200 animate-pulse"></div> <div className="name-skeleton h-6 bg-gray-200 animate-pulse w-32"></div> </div> {/* 其他布局区域的骨架屏,比如侧边栏、导航等 */} </div> ) }
两种方案都能实现布局加载时的骨架屏效果,方案一更灵活,还能优化重复请求;方案二则保留了服务端渲染的优势,适合对SEO有较高要求的场景。
内容的提问来源于stack exchange,提问作者op po
相关产品推荐
相关产品推荐

