Next.js技术问题:如何用getStaticProps获取菜单数据并传递给Layout?
解决方案
方案1:构建前预生成静态菜单数据文件
这是最彻底的SSG方案,直接在项目构建阶段把菜单数据写入本地文件,全局复用,无需每个页面单独请求。
- 编写数据拉取脚本
在项目根目录创建scripts/fetch-menu.js:
const fs = require('fs'); const path = require('path'); const client = require('../path/to/your/sanity/client'); // 替换为你的Sanity客户端路径 async function fetchMenu() { const menu = await client.fetch('*[_type == "mainMenu"][0]'); // 替换为你的菜单查询语句 const filePath = path.join(process.cwd(), 'src/data/menu.json'); fs.writeFileSync(filePath, JSON.stringify(menu)); console.log('菜单数据已预生成到src/data/menu.json'); } fetchMenu().catch(err => { console.error('拉取菜单数据失败:', err); process.exit(1); });
- 修改构建脚本
在package.json中,给build命令添加前置脚本:
{ "scripts": { "prebuild": "node scripts/fetch-menu.js", "build": "next build", // ...其他脚本 } }
- 在_app.js中引入并传递数据
// pages/_app.js import Layout from '../components/Layout'; import menuData from '../src/data/menu.json'; function MyApp({ Component, pageProps }) { return ( <Layout menu={menuData}> <Component {...pageProps} /> </Layout> ); } export default MyApp;
这样所有页面都会自动使用预生成的静态菜单数据,无需每个页面单独处理,完全符合SSG要求。
方案2:用高阶组件(HOC)封装菜单数据逻辑
如果不想修改构建流程,这个方案可以减少页面代码的重复度,避免每个页面都写重复的getStaticProps。
- 创建HOC文件
// components/withMenu.js import client from '../path/to/your/sanity/client'; export const withMenu = (PageComponent) => { const WithMenuWrapper = (props) => <PageComponent {...props} />; WithMenuWrapper.getStaticProps = async (context) => { // 保留页面自身的getStaticProps逻辑(如果有的话) const pageProps = typeof PageComponent.getStaticProps === 'function' ? await PageComponent.getStaticProps(context) : { props: {} }; // 获取菜单数据 const menu = await client.fetch('*[_type == "mainMenu"][0]'); // 替换为你的查询 return { props: { ...pageProps.props, menu, }, }; }; return WithMenuWrapper; };
- 在页面中使用HOC
// pages/your-page.js import Layout from '../components/Layout'; import { withMenu } from '../components/withMenu'; function YourPage({ menu }) { return ( <Layout menu={menu}> <article>页面内容...</article> </Layout> ); } // 用HOC包裹页面组件,自动注入menu props export default withMenu(YourPage);
这个方案虽然还是需要在页面中引入Layout,但无需重复编写菜单数据的获取逻辑,代码复用性更高。
方案3:升级到Next.js 13+ App Router(可选)
如果你的项目可以升级到App Router,Layout组件作为Server Component可以直接在构建时获取数据,完全符合SSG,且无需额外处理:
// app/layout.js import client from '../path/to/your/sanity/client'; async function RootLayout({ children }) { // Server Component在构建时自动执行fetch,生成静态数据 const menu = await client.fetch('*[_type == "mainMenu"][0]'); return ( <html lang="en"> <body> <Header menu={menu} /> {children} </body> </html> ); } export default RootLayout;
App Router的Server Component默认会静态渲染,数据在构建时获取,完美解决你的痛点,但需要适配App Router的目录结构和语法。
内容的提问来源于stack exchange,提问作者richtea
相关产品推荐
相关产品推荐

