延迟加载
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:
-
将 动态导入 与
next/dynamic
结合使用¥Using Dynamic Imports with
next/dynamic
-
将
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/dynamic
是 React.lazy()
和 悬念 的复合体。它在 app
和 pages
目录中的行为方式相同,以允许增量迁移。
¥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.
如果要禁用客户端组件的预渲染,可以使用设置为 false
的 ssr
选项:
¥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: false
与next/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 withnext/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)
)