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