在组件内部提取Next.js数据的最佳做法

如何解决在组件内部提取Next.js数据的最佳做法

我有一个全局显示的菜单组件。将数据放入该组件的最佳实践是什么?

我正在尝试利用Next.js提供的静态生成,但是Next.js团队的所有data fetching指南都与页面有关。 getStaticPropsgetStaticPaths似乎与页面生成有关,与组件数据无关。他们的SWR软件包是正确的答案,还是Apollo Client?

通常在基于钩子的React中,我只是将数据调用放入useEffect中。我不确定如何推断出一切都是在构建时使用Next渲染的。

解决方法

这是一个非常棘手的问题,我认为我们需要在解决方案成为焦点之前先布置一些背景。我专注于React.js世界,但其中很多将适用于我想象中的Vue / Nuxt。

背景/静态生成的好处:

Gatsby和Next专注于生成静态页面,这极大地提高了React.js网站的性能和SEO。除了这种简单的见解之外,这两个平台还有很多技术开销,但让我们从数字机器为浏览器抽出精美的HTML页面的想法开始。

页面数据获取

对于Next.js(自v9.5起),其数据获取机制getStaticProps为您完成了大部分繁重的工作,但已将其沙箱存储到/pages/目录中。这个想法是,它会为您进行数据获取,并在构建期间将其告知Node中的Next.js页面生成器(而不是在useEffect钩子或componentDidMount的组件端进行处理) 。 Gatsby的gatsby-node.js文件的功能与之大致相同,该文件协调数据的获取以与Node服务器协同构建页面。

需要数据的全局组件如何?

您可以同时使用Gatsby和Next来生成任何类型的网站,但是CMS驱动的网站是一个巨大的用例,因为其中大部分内容都是静态的。这些工具非常适合该用例。

在典型的CMS网站中,您将具有全局元素-页眉,页脚,搜索,菜单等。这是静态生成面临的巨大挑战:在构建时如何将数据获取到动态全局组件中?这个问题的答案是……你不知道。如果您仔细考虑一下,这是有道理的。如果您有一个10K页面的网站,如果有人将新的导航项添加到菜单中,您是否要触发整个网站的重建?

全局组件的数据获取

那么我们如何解决这个问题?我最好的答案是apollo-client并获取客户端。这对我们有很多帮助:

  • 对于小型查询,性能影响可以忽略不计。
  • 如果我们需要在CMS层重建页面以进行更改,则可以通过Next / Gatsby的检测机制进行滑动,因此我们可以进行全局更改而无需触发巨大的站点范围重建。

那么这实际上是什么样子?在组件级别,它看起来就像常规的Apollo增强组件一样。我通常使用styled-components,但尝试将其删除,以便您可以更好地了解发生了什么。

import React from 'react'
import { useQuery,gql } from '@apollo/client'
import close from '../public/close.svg'

/**
 * <NavMenu>
 * 
 * Just a typical menu you might see on a CMS-driven site. It takes in a couple of props to move state around.
 * 
 * @param { boolean } menuState - lifted state true/false toggle for menu opening/closing
 * @param { function } handleMenu - lifted state changer for menuState,handles click event
 */

const NAV_MENU_DATA = gql`
  query NavMenu($uid: String!,$lang: String!) {
    nav_menu(uid: $uid,lang: $lang) {
      main_menu_items {
        item {
          ... on Landing_page {
            title
            _linkType
            _meta {
              uid
              id
            }
          }
        }
      }
    }
  }
`

const NavMenu = ({ menuState,handleMenu }) => {
  // Query for nav menu from Apollo,this is where you pass in your GraphQL variables
  const { loading,error,data } = useQuery(NAV_MENU_DATA,{
    variables: {
      "uid": "nav-menu","lang": "en-us"
    }
  })
  
  if (loading) return `<p>Loading...</p>`;
  if (error) return `Error! ${error}`;

  // Destructuring the data object
  const { nav_menu: { main_menu_items } } = data

  // `menuState` checks just make sure out menu was turned on
  if (data) return(
    <>
      <section menuState={ menuState }>
        <div>
          { menuState === true && (
            <div>Explore</div>
          )}
          <div onClick={ handleMenu }>
          { menuState === true && (
            <svg src={ close } />
          )}
          </div>
        </div>
        { menuState === true && (
          <ul>
            { data.map( (item) => {
              return (
                <li link={ item }>
                  { item.title }
                </li>
              )
            })}
          </ul>
        )}
      </section>
    </>
  )
}

export default NavMenu

设置下一步以使用Apollo

Next.js团队实际上对此进行了很好的记录,这使我感到我并没有完全掌握该工具的工作方式。您可以在其仓库中找到great examples of using Apollo

使Apollo进入Next应用程序的步骤:

  1. 制作一个自定义的useApollo钩子,以建立与您的数据源的连接(我将自己的数据放到Next的层次结构中的/lib/apollo/apolloClient.js中,但我确信它可以在其他地方使用)。
import { useMemo } from 'react'
import { ApolloClient,InMemoryCache,SchemaLink,HttpLink } from '@apollo/client'

let apolloClient

// This is mostly from next.js official repo on how best to integrate Next and Apollo
function createIsomorphLink() {
  // only if you need to do auth
  if (typeof window === 'undefined') {
    // return new SchemaLink({ schema }) 
    return null
  } 
  // This sets up the connection to your endpoint,will vary widely.
  else {
    return new HttpLink({
      uri: `https://yourendpoint.io/graphql`
    })
  }
}

// Function that leverages ApolloClient setup,you could just use this and skip the above function if you aren't doing any authenticated routes
function createApolloClient() {
  return new ApolloClient({
    ssrMode: typeof window === 'undefined',link: createIsomorphLink(),cache: new InMemoryCache(),})
}


export function initializeApollo(initialState = null) {
  const _apolloClient = apolloClient ?? createApolloClient()

  // If your page has Next.js data fetching methods that use Apollo Client,the initial state
  // gets hydrated here
  if (initialState) {
    // Get existing cache,loaded during client side data fetching
    const existingCache = _apolloClient.extract()
    // Restore the cache using the data passed from getStaticProps/getServerSideProps
    // combined with the existing cached data
    _apolloClient.cache.restore({ ...existingCache,...initialState })
  }
  // For SSG and SSR always create a new Apollo Client
  if (typeof window === 'undefined') return _apolloClient
  // Create the Apollo Client once in the client
  if (!apolloClient) apolloClient = _apolloClient

  return _apolloClient
}

// This is goal,now we have a custom hook we can use to set up Apollo across our app. Make sure to export this!
export function useApollo(initialState) {
  const store = useMemo(() => initializeApollo(initialState),[initialState])
  return store
}
  1. 在Next的_app.js目录中修改/pages/。基本上,这是Next中每个页面周围的包装器。我们将向其中添加Apollo提供程序,现在我们可以从任何组件全局访问Apollo。
import { ApolloProvider } from '@apollo/react-hooks'
import { useApollo } from '../lib/apollo/apolloClient'

/**
 * <MyApp>
 * 
 * This is an override of the default _app.js setup Next.js uses
 * 
 * <ApolloProvider> gives components global access to GraphQL data fetched in the components (like menus)
 * 
 */
const MyApp = ({ Component,pageProps }) => {
  // Instantiates Apollo client,reads Next.js props and initialized Apollo with them - this caches data into Apollo.
  const apolloClient = useApollo(pageProps.initialApolloState)

  return (
    <ApolloProvider client={ apolloClient }>
      <Component {...pageProps} />
    </ApolloProvider>
  )
}

export default MyApp

现在,您可以使用Apollo在组件内部获取动态数据!很容易吧;)哈!

,

对于 NextJS 中的全局数据获取,我使用 react-query 并且不需要全局状态,因为它允许您缓存数据。假设您有一个包含类别的博客,并且您希望将类别名称作为下拉菜单放在 navbar 中。在这种情况下,您可以调用 API 以从 react-query 组件中获取带有 navbar 的数据并缓存它。导航栏数据可用于所有页面。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-