Skip to content

局部渲染

前言

SuspenseNext.js 项目中常用的一个组件,了解其原理和背景有助于我们正确使用 Suspense 组件。

传统 SSR

在最近的两篇文章里,我们已经介绍了 SSR 的原理和缺陷。简单来说,使用 SSR,需要经过一系列的步骤,用户才能查看页面、与之交互。具体这些步骤是:

    1. 服务端获取所有数据
    1. 服务端渲染 HTML
    1. 将页面的 HTML、CSS、JavaScript 发送到客户端
    1. 使用 HTML 和 CSS 生成不可交互的用户界面(non-interactive UI)
    1. 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>
  );
}