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>

<ComponentA />


{showMore && <ComponentB />}
<button onClick={() => setShowMore(!showMore)}>Toggle</button>


<ComponentC />
</div>
)
}

跳过 SSR

¥Skipping SSR

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

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

注意:ssr: false 选项仅适用于客户端组件,将其移入客户端组件可确保客户端代码拆分正常工作。

¥Note: ssr: false option will only work for client components, move it into client components ensure the client code-splitting working properly.

如果要禁用客户端组件的预渲染,可以使用设置为 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

如果动态导入服务器组件,则只有作为服务器组件子级的客户端组件才会被延迟加载 - 不是服务器组件本身。当你在服务器组件中使用它时,它还将帮助预加载静态资源(例如 CSS)。

¥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. It will also help preload the static assets such as CSS when you're using it in Server Components.

import dynamic from 'next/dynamic'

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

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

注意:服务器组件不支持 ssr: false 选项。如果你尝试在服务器组件中使用它,你将看到错误。服务器组件中不允许 ssr: falsenext/dynamic 一起使用。请将其移到客户端组件中。

¥Note: ssr: false option is not supported in Server Components. You will see an error if you try to use it in Server Components. ssr: false is not allowed with next/dynamic in Server Components. Please move it into a client component.

加载外部库

¥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>

<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)
)