Skip to content

第9章 流

9.1 使用loading.tsx传输整个页面

/app/dashboard/loading.tsx

tsx
export default function Loading() {
  return <div>Loading...</div>;
}

9.2 添加加载骨架

  • loading.tsx在文件系统中的级别高于/invoices/page.tsx和/customers/page.tsx,因此它也适用于这些页面.所以把loading.tsx和page.tsx文件移动到文件夹(overview)中
  • 当你使用圆括号()创建一个新文件夹时,文件夹的名称不会包含在URL路径中。所以 /dashboard/(overview)/page.tsx 变成了/dashboard

9.3 流式传输组件Suspense

/app/dashboard/(overview)/page.tsx

tsx
import { Suspense } from 'react';
import { RevenueChartSkeleton } from '@/app/ui/skeletons';
 
export default async function Page() {
  const latestInvoices = await fetchLatestInvoices();
  const {
    numberOfInvoices,
    numberOfCustomers,
    totalPaidInvoices,
    totalPendingInvoices,
  } = await fetchCardData();
 
  return (
    <main>
      <div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
        <Suspense fallback={<RevenueChartSkeleton />}>
          <RevenueChart />
        </Suspense>
        <LatestInvoices latestInvoices={latestInvoices} />
      </div>
    </main>
  );
}

9.3.1 组元件

/app/dashboard/page.tsx

tsx
import CardWrapper from '@/app/ui/dashboard/cards';
// ...
import {
  CardsSkeleton,
} from '@/app/ui/skeletons';
 
export default async function Page() {
  return (
    <main>
      <h1 className={`${lusitana.className} mb-4 text-xl md:text-2xl`}>
        Dashboard
      </h1>
      <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
        <Suspense fallback={<CardsSkeleton />}>
          <CardWrapper />
        </Suspense>
      </div>
      // ...
    </main>
  );
}

/app/ui/dashboard/cards.tsx

tsx
// ...
import { fetchCardData } from '@/app/lib/data';
 
// ...
 
export default async function CardWrapper() {
  const {
    numberOfInvoices,
    numberOfCustomers,
    totalPaidInvoices,
    totalPendingInvoices,
  } = await fetchCardData();
 
  return (
    <>
      <Card title="Collected" value={totalPaidInvoices} type="collected" />
      <Card title="Pending" value={totalPendingInvoices} type="pending" />
      <Card title="Total Invoices" value={numberOfInvoices} type="invoices" />
      <Card
        title="Total Customers"
        value={numberOfCustomers}
        type="customers"
      />
    </>
  );
}

9.3.2 决定在哪里放置Suspense boundaries

一般来说,将数据提取向下移动到需要它的组件,然后将这些组件包装在Suspense中,这是一个很好的做法。