You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在服务端为Next.js Client Components获取数据

关于Next.js App Router中服务端数据传递给客户端组件的疑问

概念定义

  • Client Components:顶部标注'use client'的文件
  • Server Components:顶部标注'use server'的文件(注:App Router中默认组件为Server Component,无需显式标注'use server')

问题背景

我未找到相关资料说明如何在服务端获取数据后直接传递给Client Components,且Client Components中禁止使用async/await函数或组件,尝试预渲染会触发如下报错:

Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.

Next.js文档给出的客户端数据获取方式为通过useEffect发起请求:

useEffect(() => {
    fetch('/api/profile-data')
      .then((res) => res.json())
      .then((data) => {
        setData(data)
        setLoading(false)
      })
  }, [])

但该方案无法实现服务端获取数据后直接填充Client Components的需求,同时原有的getStaticProps/getServerSideProps在App Router中已不再可用。

尝试的解决方案

我通过拆分文件的方式实现需求,目录结构如下:

/app
  page.tsx
  clientPage.tsx
  layout.tsx

page.tsx(Server Component)

// App Router默认是Server Component,显式标注仅作说明
"use server"

import React from 'react'
import { ClientPage } from './clientPage'

const getServerDataForClient = async () => {
  console.log('That was executed on the server side')

  const serverData = await new Promise(resolve => {
    setTimeout(() => resolve({ someData: 'secret' }), 3000)
  })

  return serverData
}

const Page = async () => {
  console.log('That also was executed on the server side')
  const serverData = await getServerDataForClient()

  return <ClientPage serverData={serverData} />
}

export default Page

clientPage.tsx(Client Component)

'use client'

import React, { FC, useEffect, useState } from 'react'

type TClientPageProps = {
  serverData: {
    someData: string
  }
}

// 首次渲染在服务端执行,会忽略浏览器专属逻辑(如useEffect、useLayoutEffect等)
// 仅返回包含初始状态(如useState的初始值、useRef)的HTML
export const ClientPage: FC<TClientPageProps> = ({ serverData }) => {
  // 服务端和浏览器首次渲染都会拿到这个初始值
  const [state, setState] = useState('server state')
  
  console.log("This log will be shown in the server logs on component's first mount and browsers on all mounts")

  useEffect(() => {
    // 这段逻辑不会在服务端执行,仅在浏览器组件挂载后触发
    setState('browser state')
  }, [])

  return (
    <main>
      <div>State is: {state}</div>
      <div>Server data is: {serverData.someData}</div>
    </main>
  )
}

这种方式通过Server Component处理服务端数据逻辑,Client Component实现UI交互,无需依赖旧的getServerSideProps/getStaticProps,但必须拆分文件。

疑问点

  1. 上述用法是否正确?有没有遗漏的注意事项?
  2. 如果存在多层嵌套组件,是不是只能通过props drilling来传递服务端数据?这一点我比较困惑。

附加测试情况

  • 若将page.tsx与clientPage.tsx合并,会触发报错:"useState" is not allowed in Server Components.
  • 尝试在Client Components中用Server Action获取数据并预渲染,代码如下:
const getServerDataForClient = async () => {
  'use server'

  console.log('That was executed on the server side')

  const serverData = await new Promise(resolve => {
    setTimeout(() => resolve({ someData: 'secret' }), 3000)
  })

  return serverData
}

会报错,因为Client Components无法用async/await等待Server Action的结果。

  • 考虑过给所有页面组件添加'use client',例如:
/src/components/Partners
"use client"

export const Partners: FC = () =>
  // some code here

但这样会降低App Router的使用体验,同一页面包含多个此类组件时还需额外创建聚合组件。

内容的提问来源于stack exchange,提问作者Ahmed Faraz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 12:23:25