You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 11:58:14