Next.js懒加载 #

一、懒加载概述 #

1.1 什么是懒加载 #

懒加载是延迟加载非关键资源的技术:

  • 减少初始加载时间
  • 按需加载代码
  • 提升用户体验

1.2 懒加载方式 #

方式 说明
dynamic() 动态导入组件
React.lazy() React原生懒加载
Suspense 加载状态显示

二、动态导入 #

2.1 基本用法 #

tsx
import dynamic from 'next/dynamic'

const DynamicComponent = dynamic(() => import('./HeavyComponent'))

export default function Page() {
    return (
        <div>
            <h1>页面标题</h1>
            <DynamicComponent />
        </div>
    )
}

2.2 加载状态 #

tsx
import dynamic from 'next/dynamic'

const DynamicComponent = dynamic(() => import('./HeavyComponent'), {
    loading: () => <p>加载中...</p>,
})

export default function Page() {
    return <DynamicComponent />
}

2.3 禁用SSR #

tsx
import dynamic from 'next/dynamic'

const DynamicChart = dynamic(() => import('./Chart'), {
    ssr: false,
    loading: () => <div className="animate-pulse h-64 bg-gray-200 rounded"></div>,
})

export default function Page() {
    return <DynamicChart />
}

2.4 命名导出 #

tsx
import dynamic from 'next/dynamic'

const NamedComponent = dynamic(
    () => import('./Components').then(mod => mod.NamedComponent)
)

export default function Page() {
    return <NamedComponent />
}

三、组件懒加载 #

3.1 模态框懒加载 #

tsx
'use client'

import dynamic from 'next/dynamic'
import { useState } from 'react'

const Modal = dynamic(() => import('./Modal'), {
    ssr: false,
})

export default function Page() {
    const [showModal, setShowModal] = useState(false)
    
    return (
        <div>
            <button onClick={() => setShowModal(true)}>
                打开模态框
            </button>
            {showModal && <Modal onClose={() => setShowModal(false)} />}
        </div>
    )
}

3.2 图表懒加载 #

tsx
import dynamic from 'next/dynamic'

const Chart = dynamic(() => import('./Chart'), {
    loading: () => <ChartSkeleton />,
    ssr: false,
})

function ChartSkeleton() {
    return (
        <div className="animate-pulse">
            <div className="h-64 bg-gray-200 rounded"></div>
        </div>
    )
}

export default function Dashboard() {
    return (
        <div>
            <h1>仪表盘</h1>
            <Chart data={chartData} />
        </div>
    )
}

3.3 编辑器懒加载 #

tsx
import dynamic from 'next/dynamic'

const Editor = dynamic(() => import('./Editor'), {
    loading: () => <div className="h-96 bg-gray-100 animate-pulse rounded"></div>,
    ssr: false,
})

export default function CreatePost() {
    return (
        <div>
            <h1>创建文章</h1>
            <Editor />
        </div>
    )
}

四、Suspense #

4.1 基本用法 #

tsx
import { Suspense } from 'react'

async function SlowComponent() {
    const data = await fetch('https://api.example.com/data')
    return <div>{data}</div>
}

export default function Page() {
    return (
        <Suspense fallback={<div>加载中...</div>}>
            <SlowComponent />
        </Suspense>
    )
}

4.2 嵌套Suspense #

tsx
import { Suspense } from 'react'

export default function Page() {
    return (
        <div>
            <h1>页面标题</h1>
            <Suspense fallback={<HeaderSkeleton />}>
                <Header />
            </Suspense>
            <Suspense fallback={<ContentSkeleton />}>
                <Content />
            </Suspense>
        </div>
    )
}

4.3 多个Suspense #

tsx
import { Suspense } from 'react'

export default function Dashboard() {
    return (
        <div className="grid grid-cols-2 gap-4">
            <Suspense fallback={<CardSkeleton />}>
                <StatsCard />
            </Suspense>
            <Suspense fallback={<CardSkeleton />}>
                <RecentOrders />
            </Suspense>
            <Suspense fallback={<ChartSkeleton />}>
                <SalesChart />
            </Suspense>
            <Suspense fallback={<ListSkeleton />}>
                <RecentActivity />
            </Suspense>
        </div>
    )
}

五、条件加载 #

5.1 基于条件加载 #

tsx
'use client'

import { useState } from 'react'

export default function Page() {
    const [showHeavy, setShowHeavy] = useState(false)
    
    return (
        <div>
            <button onClick={() => setShowHeavy(true)}>
                显示重型组件
            </button>
            {showHeavy && <HeavyComponent />}
        </div>
    )
}

5.2 基于路由加载 #

tsx
'use client'

import { usePathname } from 'next/navigation'
import dynamic from 'next/dynamic'

const AdminPanel = dynamic(() => import('./AdminPanel'))
const UserPanel = dynamic(() => import('./UserPanel'))

export default function Dashboard() {
    const pathname = usePathname()
    
    if (pathname.startsWith('/admin')) {
        return <AdminPanel />
    }
    
    return <UserPanel />
}

六、骨架屏 #

6.1 基本骨架屏 #

tsx
function CardSkeleton() {
    return (
        <div className="animate-pulse bg-white rounded-lg shadow p-6">
            <div className="h-4 bg-gray-200 rounded w-3/4 mb-4"></div>
            <div className="h-4 bg-gray-200 rounded w-1/2 mb-4"></div>
            <div className="h-20 bg-gray-200 rounded"></div>
        </div>
    )
}

6.2 列表骨架屏 #

tsx
function ListSkeleton() {
    return (
        <div className="animate-pulse space-y-4">
            {[...Array(5)].map((_, i) => (
                <div key={i} className="flex gap-4">
                    <div className="w-12 h-12 bg-gray-200 rounded-full"></div>
                    <div className="flex-1">
                        <div className="h-4 bg-gray-200 rounded w-1/2 mb-2"></div>
                        <div className="h-3 bg-gray-200 rounded w-3/4"></div>
                    </div>
                </div>
            ))}
        </div>
    )
}

6.3 表格骨架屏 #

tsx
function TableSkeleton() {
    return (
        <div className="animate-pulse">
            <div className="h-10 bg-gray-200 rounded mb-4"></div>
            {[...Array(5)].map((_, i) => (
                <div key={i} className="flex gap-4 mb-2">
                    <div className="h-4 bg-gray-200 rounded w-1/4"></div>
                    <div className="h-4 bg-gray-200 rounded w-1/3"></div>
                    <div className="h-4 bg-gray-200 rounded w-1/4"></div>
                    <div className="h-4 bg-gray-200 rounded w-1/6"></div>
                </div>
            ))}
        </div>
    )
}

七、最佳实践 #

7.1 按需加载 #

tsx
import dynamic from 'next/dynamic'

const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
    loading: () => <Skeleton />,
})

export default function Page() {
    return (
        <div>
            <h1>页面标题</h1>
            <HeavyComponent />
        </div>
    )
}

7.2 使用Suspense #

tsx
import { Suspense } from 'react'

export default function Page() {
    return (
        <Suspense fallback={<Loading />}>
            <AsyncComponent />
        </Suspense>
    )
}

7.3 提供加载状态 #

tsx
import dynamic from 'next/dynamic'

const Component = dynamic(() => import('./Component'), {
    loading: () => <div className="animate-pulse h-32 bg-gray-200 rounded"></div>,
})

八、总结 #

懒加载要点:

要点 说明
dynamic() 动态导入组件
ssr: false 禁用SSR
loading 加载状态
Suspense React原生
骨架屏 优化用户体验

下一步,让我们学习部署与扩展!

最后更新:2026-03-28