Next.js:减少数据获取并在页面之间共享数据

eiv*_*dml 37 fetch reactjs redux next.js sanity

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

我遇到的问题

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

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

我正在寻找的答案

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

到目前为止我的想法

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

  1. 在页脚组件(客户端)内进行提取
  2. 在所有/页面上的 getInitialProps(服务器端和客户端)中执行提取
  3. 在 _app.js 中使用 HOC 进行 fetch 并挂钩并将其getInitialProps()添加到 props,因此数据可用于所有页面
  4. 使用 zeit/swr 和数据预取来缓存数据
  5. 使用 redux 存储全局状态

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

  1. 简单的!提取代码仅在一处,它位于使用它的地方。页面加载后获取数据,因此内容“跳转”查看。数据一直在重新获取。
  2. 简单的!在服务器上获取数据,因此在呈现页面之前内容可用。为每个页面重新获取数据。我们必须记住为他们的getInitialProps().
  3. 我们可以在一个地方进行 fetch 并将其添加到所有页面 props,因此页脚数据可自动用于所有页面的 props。对于某些人来说,要轻松理解正在发生的事情可能会更复杂一些,因为它需要更多地了解 Next.js/React 的工作原理。仍然重新获取所有页面的数据。我们现在fetch()依次进行两次调用(首先在 _app.js 中加载页脚内容,然后在每个页面中获取自定义内容),所以它甚至更慢。
  4. 有点简单。我们甚至可以在加载 JS 之前使用预取将数据加载到缓存。在第一页加载后,我们将有快速的数据获取。可以直接在页脚组件中获取代码。该rel="preload"预取技术不会与所有类型的获取(使用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
Run Code Online (Sandbox Code Playgroud)

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

Hoo*_*man 23

O'right! I found this thread while I was looking for something else. But since I had to work on similar issues, I can give you some directions, and I will do my best to make it clear for you.

So there are some data which you want to have it share, across your app (pages/components).

  1. Next.js uses the App component to initialize pages. You can override it and control the page initialization. to achieve that simply create _app.js file in root of pages directory. For more information follow this link: https://nextjs.org/docs/advanced-features/custom-app
  2. Just like the way you can use getInitialProps in your pages to fetch data from your API, you can also use the same method in _app.js. So, I would fetch those data which I need to share them across my app and eliminate my API calls.

Well, Now I can think of two ways to share the data across my app

  1. Using of createContext hooks.

1.1. Create a DataContext using createContext hooks. and wrap <Component {...pageProps} /> with your <DataContext.Provider>. Here is a code snippet to give you a better clue:

<DataContext.Provider value={{ userData, footerData, etc... }}>
  <Component {...pageProps} />
</DataContext.Provider>
Run Code Online (Sandbox Code Playgroud)

1.2. Now in other pages/components you can access to your DataContext like following:

const { footerData } = useContext(DataContext); 
Run Code Online (Sandbox Code Playgroud)

And then you are able to do the manipulation in your front-end

  1. populates props using getInitialProps

2.1. getInitialProps is used to asynchronously fetch some data, which then populates props. that would be the same case in _app.js.

The code in your _app.js would be something like this:

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

2.2. Now in your pages (not in your components) you can access to props including those you have shared from _app.js and you can start to do you manipulation.

Hope I could give you a clue and direction. Have fun exploring.

  • 为了回应@EthanRyan,这只在没有静态生成的页面中禁用它(https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static- Generation),而不是所有页面。使用 getStaticProps 的页面仍将静态生成。 (9认同)
  • 在 _app 中使用 getInitialProps 的问题是它禁用了自动静态优化。因此,所有页面都将在服务器端呈现,这意味着您错过了静态生成页面的好处。来自 Next.js 文档:“注意事项如果您有一个带有 getInitialProps 的自定义应用程序,那么此优化将在没有静态生成的页面中关闭。” - https://nextjs.org/docs/advanced-features/automatic-static-optimization (7认同)
  • 你是对的@i-know-nothing,我无法编辑我的评论,但如果可以的话,我会将其修改为:“因此,所有未明确使用 getStaticProps 的页面都将在服务器端呈现。” (3认同)
  • 如果我想将此数据传递给导航组件怎么办? (2认同)
  • 假设您的索引页包含相应的组件。&lt;Header /&gt; &lt;Main /&gt; &lt;Footer /&gt; 您可以访问索引中的道具,现在您想做的就是将其传递给组件。因此,您可以像这样轻松处理它: &lt;Header data={props.headerData} /&gt; 在 Header 组件内,您可以拥有像导航栏这样的子组件,您现在可以在其中从标题数据中提取数据并将其分配给您的 Nav 组件。因此,我们可以说,这就是您的标头组件 &lt;Nav data={data.NavData} /&gt; 中的样子,希望我能为您提供线索。 (2认同)