如何从远程GraphQL获取数据作为全局值在应用中使用?
解决Next.js全局获取CMS GraphQL数据的方案
你想要在所有页面无需重复写获取代码就能访问远程CMS的GraphQL数据,之前用global对象的方法因为异步问题走不通,下面给你几个靠谱的实现方式:
一、React Context全局管理(通用所有Next.js版本)
这个方法适合数据可能动态更新,且需要在客户端、服务器端都能访问的场景,页面里完全不用写数据获取代码:
- 创建全局数据Context
新建contexts/GlobalDataContext.js文件:
import { createContext, useContext } from 'react'; const GlobalDataContext = createContext(null); export function GlobalDataProvider({ children, data }) { return ( <GlobalDataContext.Provider value={data}> {children} </GlobalDataContext.Provider> ); } export function useGlobalData() { return useContext(GlobalDataContext); }
- 在_app.js中统一获取数据并注入Context
如果是Pages Router的_app.js,用getInitialProps在页面初始化前完成数据获取:
import { GlobalDataProvider } from '../contexts/GlobalDataContext'; import request from 'graphql-request'; import { gql } from 'graphql-request'; MyApp.getInitialProps = async () => { const mydata = await request( 'https://example.com/graphql', gql` query Data { mydata } ` ); return { globalData: mydata }; }; function MyApp({ Component, pageProps, globalData }) { return ( <GlobalDataProvider data={globalData}> <Component {...pageProps} /> </GlobalDataProvider> ); } export default MyApp;
- 任意页面直接使用数据
import { useGlobalData } from '../contexts/GlobalDataContext'; function SomePage() { const globalData = useGlobalData(); return <div>{globalData.mydata}</div>; } export default SomePage;
二、构建时预取静态数据(适合不常更新的内容)
如果你的CMS数据很少变动,可以在构建阶段就把数据拉取下来,通过环境变量全局访问:
- 在next.config.js中获取数据并注入环境变量
const { request, gql } = require('graphql-request'); module.exports = async () => { // 构建阶段异步获取CMS数据 const mydata = await request( 'https://example.com/graphql', gql` query Data { mydata } ` ); return { env: { // 加NEXT_PUBLIC_前缀让客户端能访问 NEXT_PUBLIC_GLOBAL_DATA: JSON.stringify(mydata), }, // 其他Next.js配置项 }; };
- 页面中直接读取环境变量
const globalData = JSON.parse(process.env.NEXT_PUBLIC_GLOBAL_DATA); function SomePage() { return <div>{globalData.mydata}</div>; } export default SomePage;
注意:这种方式的数据是构建时固化的,CMS数据更新后需要重新构建部署才能生效。
三、Next.js 13+ App Router 用Server Component布局
如果用App Router,直接在根布局里以Server Component的身份获取数据,天然支持异步,不用额外处理:
- 在app/layout.js中获取数据
import { request, gql } from 'graphql-request'; import { GlobalDataProvider } from './contexts/GlobalDataContext'; export default async function RootLayout({ children }) { // Server Component中直接异步获取数据 const globalData = await request( 'https://example.com/graphql', gql` query Data { mydata } ` ); return ( <html lang="zh-CN"> <body> <GlobalDataProvider data={globalData}> {children} </GlobalDataProvider> </body> </html> ); }
之后页面同样通过useGlobalData Hook获取数据即可,完全不用写获取逻辑。
为什么之前的global方法行不通?
- 服务器端的
global和客户端的window是完全独立的上下文,客户端根本访问不到服务器端赋值的global.my_data。 - 异步获取数据时,页面渲染流程不会等待异步操作完成,要么
global.my_data是未解析的Promise(没加await),要么页面已经渲染完成了数据还没赋值(加了await但时机不对)。
内容的提问来源于stack exchange,提问作者Laurence Cope
相关产品推荐
相关产品推荐

