Next.js - 页脚/页眉仅通过 API 加载一次,服务器端

Posted

技术标签:

【中文标题】Next.js - 页脚/页眉仅通过 API 加载一次,服务器端【英文标题】:Next.js - Footer / Header loading via API only once, server side 【发布时间】:2021-07-29 12:48:21 【问题描述】:

我正在构建一个 Next.js 无头应用程序,在该应用程序中,我通过对 Umbraco 后端的 API 调用获取数据。我使用 getServerSideProps 为我的每个页面加载数据,然后将其作为“数据”传递到功能组件和页面中。

我遇到的问题是网站的页眉/页脚部分有单独的端点,并且在所有页面之间共享。因此,每页执行 3 次调用(页眉、数据、页脚)是一种耻辱和不好的做法。

为了获得一次页眉/页脚,然后在多个页面中保持它,同时保持 s-s-r,可以做些什么? (重要)。我尝试过使用 cookie,但它们无法保存这么多数据。下面是一些代码:

页面抓取数据:

export async function getServerSideProps( locale ) 
    const footer = await Fetcher(footerEndPoint, locale);

    return 
        props: 
            locale: locale,
            footer: footer
        
    

布局

const Layout = (props) => 
    const  children, footer  = props;

    return (
        <>
            <Header />
            <main>
                children
            </main>
            <Footer footer=footer />
        </>
    );
;

export default Layout;

【问题讨论】:

您是否考虑过创建一个上下文以在全球范围内共享这些数据? 我同意@Gabriel22233 你可以创建一个上下文 【参考方案1】:

我看到了三个选项来实现一次仅 s-s-r 的数据获取,这些数据在页面转换之间永远不会改变:

1。 _app.ts 中的 getInitialProps()

您可以在 _app.tsx 中使用 getInitialProps()。这首先在服务器上运行,您可以将响应值缓存在变量中。下次执行getInitialProps() 时,它将只提供缓存的值,而不是触发另一个请求。要使这项工作在客户端工作,您必须在 useEffect 中重新水化缓存变量:

// pages/_app.tsx
let navigationPropsCache

function MyApp( Component, pageProps, navigationProps ) 
  
  useEffect(
    ()=>
      navigationPropsCache = navigationProps
    , 
    []
  )
    
    return <>
      <Navigation items=navigationProps/>
      <Component ...pageProps />
  </>


MyApp.getInitialProps = async () => 
  if(navigationPropsCache) 
    return navigationProps: navigationPropsCache
  

  const res = await fetch("http://localhost:3000/api/navigation")
  const navigationProps = await res.json()
  navigationPropsCache = navigationProps

  return navigationProps

请注意,getInitialProps() 是自下一个 9.3 起已弃用的功能。不知道将来会支持多长时间。见:https://nextjs.org/docs/api-reference/data-fetching/getInitialProps

完整代码示例请参见https://github.com/breytex/firat500/tree/trivial-getInitialProps。

2。使用自定义的下一个服务器实现

这个解决方案基于两个想法:

    使用自定义 server.ts 拦截 nextjs s-s-r 功能。获取您需要的所有数据,渲染导航栏和页脚服务器端,将组件 html 注入 s-s-r 结果。 根据您还以&lt;script&gt; 附加到 DOM 的获取数据的字符串化版本对 DOM 进行再水化。
// server/index.ts

  server.all("*", async (req, res) => 
    const html = await app.renderToHTML(req, res, req.path, req.query);
    const navigationProps = await getNavigationProps()

    const navigationHtml = renderToString(React.createElement(Navigation, items: navigationProps))

    const finalHtml = html
      .replace("</body>", `<script>window.navigationProps = $JSON.stringify(navigationProps);</script></body>`)
      .replace("navigation-placeholder", navigationHtml)

    return res.send(finalHtml);
  );

// components/Navigation.tsx

export const Navigation: React.FC<Props> = (items)=>
  const [finalItems, setFinalItems] = useState(items ?? [])

  useEffect(
    ()=>
      setFinalItems((window as any).navigationProps)
    ,
    []
  )

  if(!Array.isArray(finalItems) || finalItems.length === 0) return <div>"navigation-placeholder"</div>

  return (
    <div style=display:"flex", maxWidth: "500px", justifyContent: "space-between", marginTop: "100px">
      finalItems.map(item => <NavigationItem ...item/>)
    </div>
  )


我现在认为这是一个非常肮脏的例子,但你可以在此基础上构建一些强大的东西。

在此处查看完整代码:https://github.com/breytex/firat500/tree/next-link-navigation

3。使用 react-s-s-r-prepass 执行所有数据获取服务器端

这使用了一个定制的 fetch wrapper,它有某种缓存 服务器端遍历React组件树,执行所有数据获取函数。这会填充缓存。 缓存的状态被发送到客户端并重新水化客户端缓存 在 DOM 补水时,所有数据都从该缓存中提供,因此不会再次发送请求

这个例子有点长,基于urql项目的杰出工作:https://github.com/FormidableLabs/next-urql/blob/master/src/with-urql-client.tsx

在此处查看完整示例:https://github.com/breytex/firat500/tree/prepass

结论:

只要可行,我个人会选择选项#1。 #3 看起来是一种具有良好开发人员体验的方法,适合更大的团队。 #2 需要一些爱才能真正有用:D

【讨论】:

第一个选项就像一个魅力。非常感谢:) 请尽可能避免使用自定义服务器,这确实是一种反模式,几乎没有任何用例,除非对不支持的功能进行紧急编码,如果这是生死攸关的问题。

以上是关于Next.js - 页脚/页眉仅通过 API 加载一次,服务器端的主要内容,如果未能解决你的问题,请参考以下文章

Codeigniter 加载页眉和页脚一次

如何仅在正文上启用反弹过度滚动(而不是页眉和页脚)?

Nginx 花式索引页眉和页脚从不加载

如何自动加载页眉和页脚视图?

重新加载UICollectionView页眉或页脚?

在不重新加载整个表格视图的情况下更改 UITableView 的部分页眉/页脚标题