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

Νίκ*_*λος 2 reactjs server-side-rendering next.js

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

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

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

页面获取数据:

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

    return {
        props: {
            locale: locale,
            footer: footer
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

布局

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

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

export default Layout;
Run Code Online (Sandbox Code Playgroud)

bre*_*tex 10

我看到了三个选项来实现仅 SSR 数据获取一次,以获取在页面转换之间永远不会改变的内容:

1. _app.ts 中的 getInitialProps()

您可以只getInitialProps()在 _app.tsx 中使用。它首先在服务器上运行,您可以将响应值缓存在变量中。下次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}
}
Run Code Online (Sandbox Code Playgroud)

getInitialProps()请注意,自 9.3 版本以来,这是一个已弃用的功能。不确定将来会支持多久。请参阅:https ://nextjs.org/docs/api-reference/data-fetching/getInitialProps

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

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

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

  1. 使用自定义的server.ts来拦截nextjs SSR功能。获取您需要的所有数据,在服务器端渲染导航栏和页脚,将组件 HTML 注入 SSR 结果中。
  2. 根据所获取数据的字符串化版本重新水化 DOM,您还以<script>.
// 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);
  });

Run Code Online (Sandbox Code Playgroud)
// 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>
  )
}

Run Code Online (Sandbox Code Playgroud)

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

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

3.使用react-ssr-prepass执行所有数据获取服务器端

  • 这使用了一个定制的获取包装器,它具有某种缓存
  • 服务器端遍历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