54

我正在寻找在 Next.js 应用程序中更好地获取数据的解决方案。在这个问题中,我不只是在寻找解决方案,而是在寻找多种选择,以便我们了解利弊。

我遇到的问题

现在我有几个页面都包含一个显示 som 静态内容的组件和一个具有从 API 获取的一些动态内容的组件。每个页面都做一个fetch()自己getInitialProps()的页面数据,还有页脚数据,这对所有页面都是一样的

这当然有效,但是有很多重复的数据获取。页脚数据将始终为所有页面显示并且始终相同。它也很少在 API 中更改,因此无需重新验证数据。

我正在寻找的答案

我不只是想解决这个问题,我也在寻找一个概述,以便为未来的项目学习一些新的实践。我喜欢编写“显而易见”的代码,所以不要寻找过于 hacky 的解决方案,例如写入window对象等。首选具有较少依赖性的简单解决方案。目标是一个快速的网站。减少网络使用/API 调用并不那么重要。

到目前为止我的想法

这是我想出的可能的解决方案,从简单/明显到更复杂。

  1. 在 Footer 组件(客户端)内部进行 fetch
  2. 在所有 /pages 上获取 getInitialProps(服务器端和客户端)
  3. 使用 HOC 在 _app.js 中进行 fetch 并连接到它getInitialProps()并将其添加到 props 中,因此所有页面都可以使用数据
  4. 使用 zeit/swr 和数据预取来缓存数据
  5. 使用 redux 存储全局状态

所有这些“工作”,但其中大多数将不必要地重新获取数据,和/或增加一点复杂性。这是我所看到的优点/缺点(数字与上面相同):

  1. 简单的!获取代码只在一个地方,它位于使用它的地方。页面加载后获取数据,因此内容“跳转”到查看。一直在重新获取数据。
  2. 简单的!数据是在服务器上获取的,因此在呈现页面之前内容是可用的。为每一页重新获取数据。我们必须记住为它们的每个页面获取相同的页脚数据getInitialProps()
  3. 我们可以在一个地方进行提取并将其添加到所有页面道具中,因此页脚数据自动适用于所有页面的道具。对于某些人来说,要轻松理解正在发生的事情可能有点复杂,因为它需要更多地了解 Next.js/React 的工作原理。仍然重新获取所有页面的数据。我们现在fetch()彼此接连进行两次调用(首先在 _app.js 中加载页脚内容,然后在每个页面中获取自定义内容),因此速度更慢。
  4. 有点简单。我们甚至可以在加载 JS 之前使用预取来加载数据到缓存。第一个页面加载后,我们将快速获取数据。可以直接在页脚组件中获取代码。预rel="preload"取技术不适用于所有类型的提取(例如 Sanity 的客户端使用 groq)。为了没有在初始页面加载后加载数据的“跳跃”内容,我们应该提供useSWR()仍然initialData需要我们在其中获取数据的getInitialProps()内容,但仅在服务器端执行此操作就足够了。可以使用新的getServerSideProps().
  5. 我们可以加载一次数据(?)并在整个应用程序中使用它。快速且更少/无重新获取。添加外部依赖。更复杂的是你必须学习 redux,甚至只加载一个共享数据对象。

当前解决方案,使用要点 2 中描述的解决方案。

const HomePage = (props) => {
  return (
    <Layout data={props.footer}>
       <Home data={props.page} />
    </Layout>
  )
}

// Not actual query, just sample
const query = `{
  "page": *[_type == "page"][0], 
  "footer": *[_type == "footer"][0]
}`

HomePage.getInitialProps = async () => {
  const data = await client.fetch(query)

  return {
    page: data.page
    footer: data.footer
  }
}

export default HomePage

希望对此有更多的了解。我缺少明显的东西?

4

1 回答 1

36

对了!我在寻找其他东西时发现了这个线程。但由于我不得不处理类似的问题,我可以给你一些方向,我会尽力为你说清楚。

因此,您希望在您的应用程序(页面/组件)中共享一些数据。

  1. Next.js 使用 App 组件来初始化页面。您可以覆盖它并控制页面初始化。要实现这一点,只需_app.js在目录的根pages目录中创建文件。有关更多信息,请点击此链接:https ://nextjs.org/docs/advanced-features/custom-app
  2. 就像您可以getInitialProps在页面中使用从 API 获取数据的方式一样,您也可以在_app.js. 所以,我会获取那些我需要在我的应用程序中共享它们的数据,并消除我的 API 调用。

好吧,现在我可以想出两种方法来在我的应用程序中共享数据

  1. 使用createContext挂钩。

1.1。使用 createContext 挂钩创建 DataContext。并<Component {...pageProps} />用你的<DataContext.Provider>. 这是一个代码片段,可以为您提供更好的线索:

<DataContext.Provider value={{ userData, footerData, etc... }}>
  <Component {...pageProps} />
</DataContext.Provider>

1.2. 现在在其他页面/组件中,您可以访问您的 DataContext,如下所示:

const { footerData } = useContext(DataContext); 

然后你就可以在你的前端进行操作了

  1. 填充props使用getInitialProps

2.1。getInitialProps用于异步获取一些数据,然后填充props. 这将是相同的情况_app.js

您的代码_app.js将是这样的:

function MyApp({ Component, pageProps, footerData }) {
  //do other stuffs
  return (
   <Component {...pageProps} footerData={footerData} />
  ;
}

MyApp.getInitialProps = async ({ Component, ctx }) => {
  const footerRes = await fetch('http://API_URL');
  const footerData = await footerRes.json(); 
  let pageProps = {};
  if (Component.getInitialProps) {
    pageProps = await Component.getInitialProps(ctx);
  }
  return { pageProps, footerData };
};

2.2. 现在在您的页面中(而不是在您的组件中),您可以访问道具,包括您共享的道具,_app.js 并且您可以开始进行操作。

希望我能给你一个线索和方向。尽情探索吧。

于 2020-04-09T22:35:56.163 回答