Skip to main content

加载UI和流式处理

特殊文件loading.js伴随着 React Suspense帮助您创建重要的加载 UI。按照这个约定,您能够在路由段的内容加载时显示一个来自服务器的立即加载状态。一旦渲染完成,会立即换为新内容。

Loading UI

即使加载状态

即使加载状态是导航时立即显示的备选 UI. 您能够预渲染加载指示器,如 skeletons 和 spinners, 或是之后屏幕中小的但重要的部分,如封面照片、标题等。这有助于用户了解应用程序正在响应并提供了更好的用户体验。

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

loading.js special file
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>边界中。

loading.js overview

您需要知道:

  • 导航是即时的,即使使用服务器为中心的路由也是如此。
  • 导航是可中断的,意味着不需要等待路由的内容充分加载再导航到其它路由。
  • 新路由段加载时,共享路由保持可交互性。

建议: 为路由段(layouts and pages)使用loading.js 约定,因为 Next.js 优化了此功能。

带有 Suspense 的 Streaming

除了loading.js, 您能够为您的 UI 组件手动创建 Suspense 边界。App Router 支持为Node.js 和 Edge runtimes使用Suspense的 streaming。

您需要了解:

  • 某些浏览器会缓冲流响应。您可能直到响应超出 1024 字节后才会看到流响应。这通常只会影响“hello world”应用程序,而不是真正的应用程序。

什么是 Streaming?

为了了解 Streaming 在 React 和 Next.js 中的工作原理, 了解**Server-Side Rendering (SSR)**和它的限制会很有帮助。

使用 SSR,在用户看见并与页面交互前有一系列步骤需要完成。

  1. 首先, 从服务器获取给定页面的所有数据。
  2. 然后,服务器为页面渲染 HTML。
  3. 页面的 HTML, CSS, 和 JavaScript 将发送到客户端。
  4. 使用生成的 HTML 和 CSS 显示非交互的用户界面。
  5. 最后, React hydrates用户界面,使其具有交互性。
Chart showing Server Rendering without Streaming

这些步骤是依次发生并阻塞性的,意味着服务器只有在获取了所有数据后才能为页面渲染 HTML。而且,在客户端,React 只有在下载了页面中所有组件的代码后才能激活 UI。

带有 React 和 Next.js 的 SSR 通过向用户显示非交互页面来帮助提升加载性能。

Server Rendering without Streaming

然而, 它可能还是很慢,因为在用户看到页面前,需要完成从服务器上获取所有数据。

Streaming 让您将页面的 HTML 拆分为较小的块并逐步将这些块从服务器发送到客户端。

How Server Rendering with Streaming Works

这样可以更快地显示页面的某些部分, 而无需等待所有数据加载完成才渲染 UI。

Streaming 与 React's 组件模型配合的很好,因为可以将每个组件视为一个 chunk。可能优先发送(如:layout)有更高优先权(如:产品信息)的或不依赖数据的组件, 且 React 可能更早地开始冻结。在同样的服务器请求中,可能在数据获取后才发送优先权较低的组件(如:评论和相关产品)。

Chart showing Server Rendering with Streaming

例如

<Suspense>的工作原理是通过包裹一个执行异步操作的组件(如:获取数据), 在异步操作发生时展示 fallback UI (如: skeleton 和 spinner), 然后在操作完成后更换组件。

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