特殊文件 loading.js 可帮助您使用 React Suspense 创建有意义的加载用户界面。使用此约定,您可以在加载路由段内容时显示来自服务器的即时加载状态。渲染完成后,新的内容会自动切换进来。
即时加载状态
即时加载状态是在导航时立即显示的后备用户界面。您可以预先渲染加载指示器,如骨架和旋转器,或未来屏幕中很小但有意义的部分,如封面照片、标题等。这可以帮助用户了解应用程序的响应情况,并提供更好的用户体验。
在文件夹中添加 loading.js 文件,创建加载状态。
// app/dashboard/loading.tsx
export default function Loading() {// You can add any UI inside Loading, including a Skeleton.return <LoadingSkeleton />
}
在同一文件夹中,loading.js 将嵌套在 layout.js 中。它将自动用 <Suspense> 边界包裹 page.js 文件及其下面的任何子文件。
注意:
- 即使是以服务器为中心的路由,导航也是即时的。
- 导航是可中断的,这意味着更改路由时无需等待路由内容加载完毕后再导航到另一条路由。
- 在加载新路由段时,共享布局仍可保持互动。
- 建议 对路由段(布局和页面)使用 loading.js 约定,因为 Next.js 优化了这一功能。
Streaming with Suspense
什么是流式处理?
要了解 React 和 Next.js 中的流如何工作,了解服务器端渲染(SSR)及其局限性很有帮助。
使用 SSR 时,在用户看到页面并与之交互之前需要完成一系列步骤:
- 首先,在服务器上获取指定页面的所有数据。
- 然后,服务器渲染页面的 HTML。
- 页面的 HTML、CSS 和 JavaScript 发送到客户端。
- 使用生成的 HTML 和 CSS 显示非交互式用户界面。
- 最后,React 对用户界面进行水合处理,使其具有交互性。
这些步骤都是顺序和阻塞性的,这意味着只有在获取了所有数据后,服务器才能渲染页面的 HTML。而在客户端,React 只有在页面中所有组件的代码都下载完毕后,才能水合用户界面。
使用 React 和 Next.js 的 SSR 可以尽快向用户显示非交互式页面,从而帮助提高感知加载性能。
但是,由于在向用户显示页面之前需要完成服务器上的所有数据获取,因此速度仍然会很慢。
通过流式传输,可以将页面的 HTML 分解成较小的块,并逐步将这些块从服务器发送到客户端。
这样就能更快地显示页面的部分内容,而无需在渲染任何用户界面之前等待加载所有数据。
流媒体与 React 的组件模型配合得很好,因为每个组件都可以被视为一个块。优先级较高的组件(如产品信息)或不依赖数据的组件可以先发送(如布局),这样 React 就可以提前开始水合。优先级较低的组件(如评论、相关产品)可以在获取数据后在同一个服务器请求中发送。
当你想防止长时间的数据请求阻塞页面渲染时,流媒体尤其有用,因为它可以缩短第一个字节时间(TTFB)和第一个内容画图时间(FCP)。它还有助于缩短交互时间(TTI),尤其是在速度较慢的设备上。
Demo
<Suspense>的工作原理是包装执行异步操作(例如获取数据)的组件,在执行异步操作时显示回退UI(例如骨架、微调器),然后在操作完成后交换组件。
// app/dashboard/page.tsx
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的优点:
流式服务器渲染--逐步将 HTML 从服务器渲染到客户端。
选择性水合(Selective Hydration)--React 会根据用户的交互情况,优先选择哪些组件进行交互。
SEO
Next.js 将等待 generateMetadata 中的数据获取完成后,再将用户界面流式传输到客户端。这将确保流式响应的第一部分包含 <head> 标记。
由于流式响应是服务器渲染的,因此不会影响搜索引擎优化。您可以使用 Google 提供的移动友好测试工具来查看您的页面在 Google 网络爬虫眼中的显示效果,并查看序列化 HTML(源代码)。