Next.js 14 App Router:服务器组件中layout.js如何向子组件传数据?
Next 14 App Router 服务器组件场景下从Layout向子组件传数据的方法
在Next 14的App Router中,只要子组件保持服务器组件身份(不使用'use client'指令、不依赖客户端专属API),完全可以从Layout向其传递数据,以下是几种可行方案:
1. 直接通过Props传递(适用于Layout内的自定义服务器组件)
如果子组件是Layout中直接引入的自定义组件,直接通过props传递数据即可,这是最直观的方式。
// app/layout.js(服务器组件) export default async function RootLayout({ children }) { // 服务器端获取数据 const layoutData = await fetch('https://api.example.com/layout-data').then(res => res.json()); return ( <html> <body> {/* 将数据通过props传给自定义服务器子组件 */} <SiteHeader data={layoutData.header} /> {children} </body> </html> ); } // app/components/SiteHeader.js(服务器组件) export default function SiteHeader({ data }) { return ( <header> <h1>{data.title}</h1> <p>{data.description}</p> </header> ); }
2. 使用React Context传递(适用于深层嵌套的服务器组件)
如果需要向多层嵌套的服务器组件传递数据,避免props drilling,可以使用React Context。注意Context的提供者和消费者都必须是服务器组件。
// app/contexts/LayoutContext.js import { createContext, useContext } from 'react'; const LayoutContext = createContext(); export function LayoutProvider({ children, data }) { return ( <LayoutContext.Provider value={data}> {children} </LayoutContext.Provider> ); } export function useLayoutData() { return useContext(LayoutContext); } // app/layout.js(服务器组件) import { LayoutProvider } from './contexts/LayoutContext'; export default async function RootLayout({ children }) { const layoutData = await fetch('https://api.example.com/layout-data').then(res => res.json()); return ( <html> <body> <LayoutProvider data={layoutData}> {children} </LayoutProvider> </body> </html> ); } // app/components/DeepNestedComponent.js(服务器组件) import { useLayoutData } from '../contexts/LayoutContext'; export default function DeepNestedComponent() { const data = useLayoutData(); return <div>{data.footerText}</div>; }
3. 共享缓存的数据获取函数(适用于路由组件如page.js)
对于路由匹配的组件(比如page.js),无法直接通过props接收Layout的数据,此时可以通过缓存的共享函数获取同一份数据,Next会自动缓存结果,避免重复请求。
// app/lib/data.js import { unstable_cache } from 'next/cache'; // 缓存数据获取函数,设置缓存有效期 export const getLayoutData = unstable_cache( async () => { const res = await fetch('https://api.example.com/layout-data'); return res.json(); }, ['layout-data-cache-key'], { revalidate: 3600 } // 1小时重新验证一次 ); // app/layout.js(服务器组件) import { getLayoutData } from './lib/data'; export default async function RootLayout({ children }) { const layoutData = await getLayoutData(); return ( <html> <body> <header>{layoutData.siteName}</header> {children} </body> </html> ); } // app/page.js(服务器组件) import { getLayoutData } from './lib/data'; export default async function HomePage() { // 调用同一份缓存函数,不会重复发起请求 const layoutData = await getLayoutData(); return <main>{layoutData.homeContent}</main>; }
注意事项
- 所有方案都不需要将子组件标记为客户端组件,保持服务器组件身份即可。
- 如果后续子组件需要转为客户端组件,需确保传递的数据是可序列化的(不能包含函数、Symbol等非序列化值)。
内容的提问来源于stack exchange,提问作者Amarjit yadav
相关产品推荐
相关产品推荐

