如何将每页静态数据传入跨页面共享的Hero组件?
实现跨页面共享有状态Hero组件的正确方案
结合Next.js App Router的静态预取能力与React Context,可以完美解决这个问题——既保留静态页面生成特性,又能让Hero组件跨页面保持状态不重渲染,实现无缝WebGL过渡效果。具体步骤如下:
1. 创建Hero数据上下文
定义一个Context用于存储当前页面的Hero静态数据,并提供更新方法,实现页面与Hero组件间的数据传递:
// app/hero-context.js 'use client'; import { createContext, useContext, useState } from 'react'; const HeroContext = createContext({ heroData: null, updateHeroData: () => {} }); export function HeroProvider({ children }) { const [heroData, setHeroData] = useState(null); const updateHeroData = (data) => { setHeroData(data); }; return ( <HeroContext.Provider value={{ heroData, updateHeroData }}> {children} </HeroContext.Provider> ); } export function useHeroContext() { return useContext(HeroContext); }
2. 在根布局中挂载Hero组件与Context Provider
将Hero组件放在根layout.js中,确保页面切换时组件不会被卸载;同时用HeroProvider包裹整个应用,让页面可以向Context注入数据:
// app/layout.js import { HeroProvider } from './hero-context'; import Hero from './components/Hero'; export default function RootLayout({ children }) { return ( <html lang="zh-CN"> <body> <HeroProvider> <Hero /> {children} </HeroProvider> </body> </html> ); }
3. 页面预取静态Hero数据并注入Context
在[slug]/page.js中,通过服务器组件预取当前页面的Hero数据,再通过一个轻量客户端组件将数据更新到Context中:
// app/[slug]/page.js import getPage from '@/lib/cms/getPage'; import { HeroDataUpdater } from './hero-data-updater'; // 生成静态路由参数 export async function generateStaticParams() { const pages = await getPagesFromCMS(); return pages.map(page => ({ slug: page.slug })); } // 服务器组件预取页面数据 export default async function Page({ params }) { const pageData = await getPage(params.slug); const heroData = pageData.hero; return ( <> <HeroDataUpdater heroData={heroData} /> <main> {/* 页面其他内容 */} </main> </> ); }
对应的客户端更新组件:
// app/[slug]/hero-data-updater.js 'use client'; import { useHeroContext, useEffect } from '../hero-context'; export function HeroDataUpdater({ heroData }) { const { updateHeroData } = useHeroContext(); useEffect(() => { updateHeroData(heroData); }, [heroData, updateHeroData]); return null; }
4. 改造Hero为有状态组件,消费Context数据
Hero组件作为客户端组件,从Context获取数据,用useRef保存WebGL等状态,页面切换时仅更新数据并触发过渡动画,不重渲染整个组件:
// app/components/Hero.js 'use client'; import { useHeroContext, useEffect, useRef } from '../hero-context'; export default function Hero() { const { heroData } = useHeroContext(); const webglInstanceRef = useRef(null); // 保存WebGL实例或动画状态 useEffect(() => { if (!heroData || !webglInstanceRef.current) return; // 根据新的Hero数据触发WebGL无缝过渡动画 webglInstanceRef.current.triggerTransition(heroData); }, [heroData]); return ( <div className="hero"> <canvas ref={webglInstanceRef}></canvas> {heroData && ( <div className="hero-content"> {/* 根据Hero数据类型渲染对应内容 */} {heroData.type === 'image' && <img src={heroData.src} alt={heroData.alt} />} {heroData.type === 'carousel' && <Carousel items={heroData.items} />} {heroData.type === 'video' && <video src={heroData.src} muted loop playsInline />} </div> )} </div> ); }
方案优势
- 保留静态生成:所有Hero数据均在服务器端预取,不影响页面静态生成特性,避免运行时调用CMS的不合理性。
- 组件状态持久:Hero组件挂载在根布局中,页面切换时不会被卸载,WebGL等状态得以保留。
- 数据传递高效:通过Context实现页面与Hero组件的静态数据传递,逻辑清晰且性能优异。
内容的提问来源于stack exchange,提问作者philolegein
相关产品推荐
相关产品推荐

