局部渲染
前言
Suspense
是 Next.js
项目中常用的一个组件,了解其原理和背景有助于我们正确使用 Suspense
组件。
传统 SSR
在最近的两篇文章里,我们已经介绍了 SSR 的原理和缺陷。简单来说,使用 SSR,需要经过一系列的步骤,用户才能查看页面、与之交互。具体这些步骤是:
- 服务端获取所有数据
- 服务端渲染 HTML
- 将页面的 HTML、CSS、JavaScript 发送到客户端
- 使用 HTML 和 CSS 生成不可交互的用户界面(non-interactive UI)
- React 对用户界面进行水合(hydrate),使其可交互(interactive UI)
这些步骤是连续的、阻塞的。这意味着服务端只能在获取所有数据后渲染 HTML,React 只能在下载了所有组件代码后才能进行水合
Suspense(但是服务器不做渲染了)
说明
<Suspense>
允许你推迟渲染某些内容,直到满足某些条件(例如数据加载完毕)。类似懒加载(但是只能客户端渲染)
你可以将动态组件包装在 Suspense
中,然后向其传递一个 fallback UI
,以便在动态组件加载时显示。如果数据请求缓慢,使用 Suspense
流式渲染该组件,不会影响页面其他部分的渲染,更不会阻塞整个页面。
- 写个例子:
ts
import { Suspense } from "react";
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function PostFeed() {
await sleep(2000);
return <h1>Hello PostFeed</h1>;
}
async function Weather() {
await sleep(8000);
return <h1>Hello Weather</h1>;
}
async function Recommend() {
await sleep(5000);
return <h1>Hello Recommend</h1>;
}
export default function Dashboard() {
return (
<section style={{ padding: "20px" }}>
<Suspense fallback={<p>Loading PostFeed Component</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading Weather Component</p>}>
<Weather />
</Suspense>
<Suspense fallback={<p>Loading Recommend Component</p>}>
<Recommend />
</Suspense>
</section>
);
}
Suspense 如何控制渲染顺序
在刚才的例子中,我们是将三个组件同时进行渲染,哪个组件的数据先返回,就先渲染哪个组件。
但有的时候,希望按照某种顺序展示组件,比如先展示 PostFeed
,再展示 Weather
,最后展示 Recommend
,此时你可以将 Suspense
组件进行嵌套:
ts
import { Suspense } from "react";
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function PostFeed() {
await sleep(2000);
return <h1>Hello PostFeed</h1>;
}
async function Weather() {
await sleep(8000);
return <h1>Hello Weather</h1>;
}
async function Recommend() {
await sleep(5000);
return <h1>Hello Recommend</h1>;
}
export default function Dashboard() {
return (
<section style={{ padding: "20px" }}>
<Suspense fallback={<p>Loading PostFeed Component</p>}>
<PostFeed />
<Suspense fallback={<p>Loading Weather Component</p>}>
<Weather />
<Suspense fallback={<p>Loading Recommend Component</p>}>
<Recommend />
</Suspense>
</Suspense>
</Suspense>
</section>
);
}