Skip to main content

加载 UI 和流式传输

特殊文件 loading.js 可帮助你使用 React Suspense 创建有意义的加载 UI。通过此约定,你可以在加载路由段内容时显示来自服务器的 即时加载状态。渲染完成后,新内容会自动换入。

¥The special file loading.js helps you create meaningful Loading UI with React Suspense. With this convention, you can show an instant loading state from the server while the content of a route segment loads. The new content is automatically swapped in once rendering is complete.

即时加载状态

¥Instant Loading States

即时加载状态是后备 UI,在导航时立即显示。你可以预渲染加载指示器,例如骨架和旋转器,或未来屏幕的一小部分但有意义的部分,例如封面照片、标题等。这有助于用户了解应用正在响应,并提供更好的用户体验。

¥An instant loading state is fallback UI that is shown immediately upon navigation. You can pre-render loading indicators such as skeletons and spinners, or a small but meaningful part of future screens such as a cover photo, title, etc. This helps users understand the app is responding and provides a better user experience.

通过在文件夹中添加 loading.js 文件来创建加载状态。

¥Create a loading state by adding a loading.js file inside a folder.

export default function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return <LoadingSkeleton />
}
export default function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return <LoadingSkeleton />
}

在同一文件夹中,loading.js 将嵌套在 layout.js 内。它会自动将 page.js 文件和下面的所有子文件封装在 <Suspense> 边界中。

¥In the same folder, loading.js will be nested inside layout.js. It will automatically wrap the page.js file and any children below in a <Suspense> boundary.

很高兴知道:

¥Good to know:

  • 即使使用 以服务器为中心的路由,导航也是即时的。

    ¥Navigation is immediate, even with server-centric routing.

  • 导航是可中断的,这意味着更改路由不需要等待路由内容完全加载后再导航到另一条路由。

    ¥Navigation is interruptible, meaning changing routes does not need to wait for the content of the route to fully load before navigating to another route.

  • 加载新路由段时,共享布局保持交互状态。

    ¥Shared layouts remain interactive while new route segments load.

推荐:对路由段(布局和页面)使用 loading.js 约定,因为 Next.js 优化了此功能。

¥Recommendation: Use the loading.js convention for route segments (layouts and pages) as Next.js optimizes this functionality.

使用 Suspense 的流式

¥Streaming with Suspense

除了 loading.js 之外,你还可以为自己的 UI 组件手动创建 Suspense Boundaries。App Router 支持使用 悬念Node.js 和 Edge 运行时 进行流式传输。

¥In addition to loading.js, you can also manually create Suspense Boundaries for your own UI components. The App Router supports streaming with Suspense for both Node.js and Edge runtimes.

很高兴知道:

¥Good to know:

  • 一些浏览器 缓冲流响应。在响应超过 1024 字节之前,你可能看不到流式响应。这通常只影响“hello world”应用,而不影响真正的应用。

    ¥Some browsers buffer a streaming response. You may not see the streamed response until the response exceeds 1024 bytes. This typically only affects “hello world” applications, but not real applications.

什么是流式?

¥What is Streaming?

要了解流式传输在 React 和 Next.js 中的工作原理,了解服务器端渲染 (SSR) 及其限制会很有帮助。

¥To learn how Streaming works in React and Next.js, it's helpful to understand Server-Side Rendering (SSR) and its limitations.

使用 SSR,在用户可以查看页面并与页面交互之前需要完成一系列步骤:

¥With SSR, there's a series of steps that need to be completed before a user can see and interact with a page:

  1. 首先,在服务器上获取给定页面的所有数据。

    ¥First, all data for a given page is fetched on the server.

  2. 然后服务器渲染该页面的 HTML。

    ¥The server then renders the HTML for the page.

  3. 页面的 HTML、CSS 和 JavaScript 将发送到客户端。

    ¥The HTML, CSS, and JavaScript for the page are sent to the client.

  4. 使用生成的 HTML 和 CSS 显示非交互式用户界面。

    ¥A non-interactive user interface is shown using the generated HTML, and CSS.

  5. 最后,React hydrates 用户界面使其具有交互性。

    ¥Finally, React hydrates the user interface to make it interactive.

这些步骤是连续的和阻塞的,这意味着服务器只能在获取所有数据后才能渲染页面的 HTML。而且,在客户端,只有下载了页面中所有组件的代码后,React 才能对 UI 进行水合。

¥These steps are sequential and blocking, meaning the server can only render the HTML for a page once all the data has been fetched. And, on the client, React can only hydrate the UI once the code for all components in the page has been downloaded.

结合 React 和 Next.js 的 SSR 通过尽快向用户显示非交互式页面来帮助提高感知加载性能。

¥SSR with React and Next.js helps improve the perceived loading performance by showing a non-interactive page to the user as soon as possible.

但是,它仍然可能很慢,因为在向用户显示页面之前需要完成服务器上的所有数据获取。

¥However, it can still be slow as all data fetching on server needs to be completed before the page can be shown to the user.

流式传输允许你将页面的 HTML 分解为更小的块,并逐步将这些块从服务器发送到客户端。

¥Streaming allows you to break down the page's HTML into smaller chunks and progressively send those chunks from the server to the client.

这使得页面的某些部分能够更快地显示,而无需等待所有数据加载后才能渲染任何 UI。

¥This enables parts of the page to be displayed sooner, without waiting for all the data to load before any UI can be rendered.

流式处理与 React 的组件模型配合得很好,因为每个组件都可以被视为一个块。优先级较高的组件(例如产品信息)或不依赖数据的组件可以先发送(例如布局),React 可以更早地开始水合作用。具有较低优先级的组件(例如评论、相关产品)可以在获取数据后在同一服务器请求中发送。

¥Streaming works well with React's component model because each component can be considered a chunk. Components that have higher priority (e.g. product information) or that don't rely on data can be sent first (e.g. layout), and React can start hydration earlier. Components that have lower priority (e.g. reviews, related products) can be sent in the same server request after their data has been fetched.

当你想要防止长数据请求阻止页面渲染时,流式处理特别有用,因为它可以减少 第一个字节的时间 (TTFB)首次内容绘制 (FCP)。它还有助于提高 互动时间 (TTI),尤其是在速度较慢的设备上。

¥Streaming is particularly beneficial when you want to prevent long data requests from blocking the page from rendering as it can reduce the Time To First Byte (TTFB) and First Contentful Paint (FCP). It also helps improve Time to Interactive (TTI), especially on slower devices.

示例

¥Example

<Suspense> 的工作原理是封装一个执行异步操作(例如获取数据)的组件,在操作发生时显示回退 UI(例如骨架、旋转器),然后在操作完成后交换组件。

¥<Suspense> works by wrapping a component that performs an asynchronous action (e.g. fetch data), showing fallback UI (e.g. skeleton, spinner) while it's happening, and then swapping in your component once the action completes.

import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'

export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'

export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}

通过使用 Suspense,你可以获得以下好处:

¥By using Suspense, you get the benefits of:

  1. 流式服务器渲染 - 逐步将 HTML 从服务器渲染到客户端。

    ¥Streaming Server Rendering - Progressively rendering HTML from the server to the client.

  2. 选择性补水 - React 根据用户交互优先考虑哪些组件首先进行交互。

    ¥Selective Hydration - React prioritizes what components to make interactive first based on user interaction.

有关更多 Suspense 示例和用例,请参阅 React 文档

¥For more Suspense examples and use cases, please see the React Documentation.

SEO

  • Next.js 将等待 generateMetadata 内的数据获取完成,然后再将 UI 流式传输到客户端。这保证了流式响应的第一部分包含 <head> 标签。

    ¥Next.js will wait for data fetching inside generateMetadata to complete before streaming UI to the client. This guarantees the first part of a streamed response includes <head> tags.

  • 由于流式是服务器渲染的,因此不会影响 SEO。你可以使用 Google 的 丰富的结果测试 工具查看你的页面在 Google 网络爬虫程序中的显示方式,并查看序列化的 HTML (source)。

    ¥Since streaming is server-rendered, it does not impact SEO. You can use the Rich Results Test tool from Google to see how your page appears to Google's web crawlers and view the serialized HTML (source).

状态代码

¥Status Codes

流式传输时,将返回 200 状态代码以表明请求成功。

¥When streaming, a 200 status code will be returned to signal that the request was successful.

服务器仍然可以在流内容本身内向客户端传达错误或问题,例如,当使用 redirectnotFound 时。由于响应头已经发送到客户端,因此无法更新响应的状态代码。这不会影响搜索引擎优化。

¥The server can still communicate errors or issues to the client within the streamed content itself, for example, when using redirect or notFound. Since the response headers have already been sent to the client, the status code of the response cannot be updated. This does not affect SEO.