Skip to main content

延迟加载

Next.js 中的 延迟加载 通过减少渲染路由所需的 JavaScript 数量来帮助提高应用的初始加载性能。

¥Lazy loading in Next.js helps improve the initial loading performance of an application by decreasing the amount of JavaScript needed to render a route.

它允许你推迟加载客户端组件和导入的库,并且仅在需要时将它们包含在客户端包中。例如,你可能希望推迟加载模式,直到用户单击将其打开。

¥It allows you to defer loading of Client Components and imported libraries, and only include them in the client bundle when they're needed. For example, you might want to defer loading a modal until a user clicks to open it.

在 Next.js 中可以通过两种方式实现延迟加载:

¥There are two ways you can implement lazy loading in Next.js:

  1. 动态导入next/dynamic 结合使用

    ¥Using Dynamic Imports with next/dynamic

  2. React.lazy()悬念 结合使用

    ¥Using React.lazy() with Suspense

默认情况下,服务器组件自动为 代码分割,你可以使用 streaming 逐步将 UI 片段从服务器发送到客户端。延迟加载适用于客户端组件。

¥By default, Server Components are automatically code split, and you can use streaming to progressively send pieces of UI from the server to the client. Lazy loading applies to Client Components.

next/dynamic

next/dynamicReact.lazy()悬念 的复合体。它在 apppages 目录中的行为方式相同,以允许增量迁移。

¥next/dynamic is a composite of React.lazy() and Suspense. It behaves the same way in the app and pages directories to allow for incremental migration.

示例

¥Examples

导入客户端组件

¥Importing Client Components

'use client'

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

// Client Components:
const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })

export default function ClientComponentExample() {
const [showMore, setShowMore] = useState(false)

return (
<div>
{/* Load immediately, but in a separate client bundle */}
<ComponentA />

{/* Load on demand, only when/if the condition is met */}
{showMore && <ComponentB />}
<button onClick={() => setShowMore(!showMore)}>Toggle</button>

{/* Load only on the client side */}
<ComponentC />
</div>
)
}

跳过 SSR

¥Skipping SSR

当使用 React.lazy() 和 Suspense 时,客户端组件将默认预渲染(SSR)。

¥When using React.lazy() and Suspense, Client Components will be pre-rendered (SSR) by default.

如果要禁用客户端组件的预渲染,可以使用设置为 falsessr 选项:

¥If you want to disable pre-rendering for a Client Component, you can use the ssr option set to false:

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

导入服务器组件

¥Importing Server Components

如果动态导入服务器组件,则只有作为服务器组件子级的客户端组件才会被延迟加载 - 不是服务器组件本身。

¥If you dynamically import a Server Component, only the Client Components that are children of the Server Component will be lazy-loaded - not the Server Component itself.

import dynamic from 'next/dynamic'

// Server Component:
const ServerComponent = dynamic(() => import('../components/ServerComponent'))

export default function ServerComponentExample() {
return (
<div>
<ServerComponent />
</div>
)
}

加载外部库

¥Loading External Libraries

可以使用 import() 函数按需加载外部库。本例使用外部库 fuse.js 进行模糊搜索。该模块仅在用户输入搜索输入后才加载到客户端。

¥External libraries can be loaded on demand using the import() function. This example uses the external library fuse.js for fuzzy search. The module is only loaded on the client after the user types in the search input.

'use client'

import { useState } from 'react'

const names = ['Tim', 'Joe', 'Bel', 'Lee']

export default function Page() {
const [results, setResults] = useState()

return (
<div>
<input
type="text"
placeholder="Search"
onChange={async (e) => {
const { value } = e.currentTarget
// Dynamically load fuse.js
const Fuse = (await import('fuse.js')).default
const fuse = new Fuse(names)

setResults(fuse.search(value))
}}
/>
<pre>Results: {JSON.stringify(results, null, 2)}</pre>
</div>
)
}

添加自定义加载组件

¥Adding a custom loading component

import dynamic from 'next/dynamic'

const WithCustomLoading = dynamic(
() => import('../components/WithCustomLoading'),
{
loading: () => <p>Loading...</p>,
}
)

export default function Page() {
return (
<div>
{/* The loading component will be rendered while <WithCustomLoading/> is loading */}
<WithCustomLoading />
</div>
)
}

导入命名导出

¥Importing Named Exports

要动态导入命名导出,你可以从 import() 函数返回的 Promise 返回它:

¥To dynamically import a named export, you can return it from the Promise returned by import() function:

'use client'

export function Hello() {
return <p>Hello!</p>
}
import dynamic from 'next/dynamic'

const ClientComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)