与 Next.js 一同使用

App Router

Server Components

在 Next.js App Router 中,所有的组件都被默认视为 React Server Components (RSC) 。在 RSC 中您可以从 SWR 引入 SWRConfig 和序列化 Key API

import { unstable_serialize } from 'swr' // ✅ 在 Server components 中可用
import { unstable_serialize as infinite_unstable_serialize } from 'swr/infinite' // ✅ 在 Server components 中可用

import { SWRConfig } from 'swr' // ✅ 在 Server components 中可用

您不能从 SWR 引入 hook API ,因为它们在 RSC 中不可用。

import useSWR from 'swr' // ❌ 在 Server components 中不可用
import useSWRInfinite from 'swr/infinite' // ❌ 在 Server components 中不可用
import useSWRMutation from 'swr/mutation' // ❌ 在 Server components 中不可用

Client Components

您可以直接用 'use client' 标记您的组件或者从 Client Components 引入 SWR ,两种方法都允许您使用 SWR 提供的客户端数据获取 Hooks。

'use client'

import useSWR from 'swr'

export default function Page() {
  const { data } = useSWR('/api/user', fetcher)
  return <h1>{data.name}</h1>
}

在服务器组件中预获取数据

类似于 带有默认数据的预渲染 模式,使用 React Server Components (RSC) 你可以更进一步。

您可以在服务器端 启动 数据预获取,并通过 <SWRConfig> provider 的 fallback 选项将 promise 传递给客户端组件树:

import { SWRConfig } from 'swr'

export default async function Layout({ children }: { children: React.ReactNode }) {
  // 在服务器端启动数据获取
  const userPromise = fetchUserFromAPI()
  const postsPromise = fetchPostsFromAPI()

  return (
    <SWRConfig
      value={{
        fallback: {
          // 将 promise 传递给客户端组件
          '/api/user': userPromise,
          '/api/posts': postsPromise,
        },
      }}
    >
      {children}
    </SWRConfig>
  )
}

这两个数据获取函数调用 fetchUserFromAPI()fetchPostsFromAPI() 将在服务器端并行执行,因为我们不会立即 await 它们。

在 React Server Components 中,您可以跨越 "use client" 边界传递 promise,SWR 将在服务端渲染期间自动解析它们:

'use client'

import useSWR from 'swr'

export default function Page() {
  // SWR 将解析从服务器组件传递的 promise
  // 在 SSR 和客户端 hydration 期间,'user' 和 'posts' 都已准备就绪
  const { data: user } = useSWR('/api/user', fetcher)
  const { data: posts } = useSWR('/api/posts', fetcher)

  return (
    <div>
      <h1>{user.name}'s Posts</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  )
}

然后,在客户端 SWR 将接管并保持与平常一样的行为。

通过将 promise 从服务器组件传递到客户端组件,数据获取可以尽可能早地在服务器端启动。并且只有实际使用数据的 UI 边界(最近的 Suspense 边界或 Next.js layout)才会在流式 SSR 期间被阻塞。

要在应用程序中逐步采用此预获取模式,您可以启用 strictServerPrefetchWarning 选项。这将在控制台中显示警告消息,当某个 key 没有提供预填充数据时,帮助您识别哪些数据获取调用可以从服务器端预获取中受益。

客户端数据获取

如果您的页面包含一些频繁更新的数据,而您并不需要预渲染这些数据,SWR 将非常适用并且并不需要特殊的配置:只需要引入 useSWR 然后在任何需要使用数据的组件中使用这个 Hook

以下是它的工作方式:

  • 首先,立即在没有数据的情况下展示页面。您可以显示缺失数据的加载状况。
  • 然后,在客户端获取数据,并在数据准备好时展示它们.

例如,这个方法在用户仪表盘页面上工作得很好。因为仪表板是不公开、用户特定的页面,与 SEO 无关并且页面不需要预渲染。数据频繁更新,需要在请求时获取。

带有默认数据的预渲染

如果页面需要预渲染,Next.js 支持 两种预渲染方式: 静态生成 (SSG) and 服务端渲染 (SSR).

通过使用 SWR, 您可以为了 SEO 预渲染页面, 并同时在客户端享受缓存、重新验证、焦点追踪、定时重新获取等特性。

您可以使用 SWRConfigfallback 选项来将预获取的数据作为所有 SWR Hook 的初始值。

使用 getStaticProps 的例子:

 export async function getStaticProps () {
  // `getStaticProps` 在服务端执行
  const article = await getArticleFromAPI()
  return {
    props: {
      fallback: {
        '/api/article': article
      }
    }
  }
}

function Article() {
  // `data` 将始终可用,因为其处于 `fallback` 列表中
  const { data } = useSWR('/api/article', fetcher)
  return <h1>{data.title}</h1>
}

export default function Page({ fallback }) {
  // 在 `SWRConfig` 标签内的 SWR hooks 将使用这些值
  return (
    <SWRConfig value={{ fallback }}>
      <Article />
    </SWRConfig>
  )
}

该页面仍然是预先生成,SEO 友好的,并且能够快速反应。然而该页面在客户端侧仍然完全由 SWR 驱动。数据仍然是动态的,且可以自动更新。

Article 组件将先渲染预先生成的数据,并且在页面水合完成后将再次获取最新数据以保持数据最新

复杂关键字

useSWR 可以与 arrayfunction 类型的 key 一同使用. 若要通过这些类型的 key 使用预先获取的数据,需要用 unstable_serialize 序列化 fallback key.

import useSWR, { unstable_serialize } from 'swr'

export async function getStaticProps () {
  const article = await getArticleFromAPI(1)
  return {
    props: {
      fallback: {
        // unstable_serialize() array style key
        [unstable_serialize(['api', 'article', 1])]: article,
      }
    }
  }
}

function Article() {
  // using an array style key.
  const { data } = useSWR(['api', 'article', 1], fetcher)
  return <h1>{data.title}</h1>
}

export default function Page({ fallback }) {
  return (
    <SWRConfig value={{ fallback }}>
      <Article />
    </SWRConfig>
  )
}