Nextjs getInitialProps 阻止了客户端的页面渲染?

Posted

技术标签:

【中文标题】Nextjs getInitialProps 阻止了客户端的页面渲染?【英文标题】:Nextjs getInitialProps blocked the page rendering in client side? 【发布时间】:2020-06-27 17:12:57 【问题描述】:

由于我想将 s-s-r 添加到我即将进行的项目中以改进 SEO,因此我想尝试下一个。我想要的是只对初始页面使用 s-s-r,站点中的其余导航将是客户端渲染。我认为 getInitialProps 最适合这种情况,相应的文档。

据我了解,getInitialProps 在服务器中运行以进行初始页面渲染,并在使用 next/link 导航时在浏览器中运行。我发现的问题是 getInitialProps 似乎阻止了页面渲染。 (即 getInitialProps 完成后页面更改/渲染)

import axios from 'axios'

function Posts(props) 
  return (
    <div>
      <div>Posts:</div>
      <div>
        JSON.stringify(props)
      </div>
    </div>
  )


Posts.getInitialProps = async (context) => 
  const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
  // Wait longer to see the effect
  // await (new Promise((resolve) => 
  //   setTimeout(resolve, 5000)
  // ))
  return 
    props: 
      posts: response.data
    
  


export default Posts;

我怎样才能像纯 React 一样,先渲染 jsx,然后填写 props?(执行 JSON.stringify(props) 可能一开始会被忽略)

此外,在接下来的 9.3 中,团队引入了 getServerSideProps,推荐使用它而不是 getInitialProps。当它们与 getServerSideProps 在服务器上运行的不同时,它们如何具有可比性?

【问题讨论】:

getInitialProps 的结果作为 props 传递给页面组件。所以next需要等待getInitialProps解析才能渲染。这就是你所说的“阻止”吗?如果您想使用虚拟数据或其他内容进行初始渲染,则只需进行正常的反应数据获取(例如 useEffect)。 是的,我的意思是阻止。我不能做 csr,因为如果这个页面是初始页面,我需要 s-s-r。我的想法是否有效? 你能解决这个问题吗?我面临着类似的问题。 @Vishu 抱歉回复晚了,我离开了下一个。我记得,这不是我所期望的。我相信社区中的某个人会帮助你度过难关。祝你好运。 【参考方案1】:

根据您的 cmets,您希望在初始页面加载时在服务器上进行提取。但是,如果在页面之间导航,您不希望在等待 getInitialProps 返回时阻止渲染。

一种解决方案是检查您是否在服务器上,然后在getInitialProps 中进行提取。如果在客户端上,不要在getInitialProps 中进行提取,而是在您的渲染方法中使用useEffect 进行提取。

import useEffect from 'react'
import axios from 'axios'

const isServer = () => typeof window === 'undefined'

const getPosts = () => 
  return axios.get('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.data)


function Posts(posts) 
  const [renderPosts, setRenderPosts] = useState(posts)

  useEffect(() => 
    if(posts === null) 
      getPosts()
        .then(setRenderPosts)
    
  , [])

  return (
    <div>
      <div>Posts:</div>
      <div>
        JSON.stringify(renderPosts)
      </div>
    </div>
  )


Posts.getInitialProps = async (context) => 
  if(isServer()) 
    return 
      posts: await getPosts(),
    
  
  else 
    return 
      posts: null,
    
  


export default Posts

顺便说一句,你可能想在这里使用getServerSideProps,因为它只有在服务器上渲染时才会被调用。然而,当使用getServerSideProps 的页面被渲染时,它实际上会调用服务器以从getServerSideProps 获取数据,即使您使用next/link 进行导航也是如此。来自Next.js 9.3 blog post:

当使用 next/link 在页面之间导航而不是在浏览器中执行 getServerSideProps 时,Next.js 将对服务器进行获取,该服务器将返回调用 getServerSideProps 的结果。

这仍然会导致您想要避免的阻塞问题。

最后一点,这可能不是一个惯用的解决方案。可能有一个更“标准”的解决方案。我只是找不到一个。您可能还可以在页面组件周围使用包装器,它可以以更一致的方式完成所有这些工作。如果你经常使用这种模式,我会推荐它。

【讨论】:

感谢您的回答。是的,我觉得很难找到下一个资源,作为一个新人。您的意思是通过包装页面组件在 _app.js 中执行此操作吗?对于您提供的解决方案,它看起来很棒并且会起作用。但是到服务器还是会有额外的往返,不知道是不是问题。 @Jerryc 您必须与服务器对话才能获取您的数据,这是无法避免的。没有“额外”的往返行程。只需一个电话即可获取您的数据。当服务器获取数据并在初始页面加载时向您发送呈现的页面(包括数据)时,就会发生这种情况。或者,当您使用 next/link 导航并直接从 API 获取时会发生这种情况。无论哪种情况,您都会收到一个服务器调用。 抱歉,我还没试过。作为我之前的检查。客户端将始终向道具请求 page.json。但不确定是否返回空对象。如果 getinitialprops 钩子确实返回一个空对象并且客户端仍在请求 json。那么这可能是一个额外的往返。 感谢您的时间和解释。我决定使用纯反应,因为我相信 google 可以很好地反应 spa seo。 @Cully 我遇到了类似的问题,将 api 调用移动到 useEffect 可以防止渲染被阻塞。但是这样做会影响我页面的 SEO 吗?

以上是关于Nextjs getInitialProps 阻止了客户端的页面渲染?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 nextjs 的 getInitialProps 无状态组件中访问路由器参数

Nextjs getInitialProps 不适用于 NGINX

NextJS 和 Redux-thunk:出现“错误:“getInitialProps”中的循环结构”

NextJS:我是不是需要使用 getInitialProps 和 next-redux-wrapper 来向所有页面共享状态?

在 NextJS 中是不是可以让自定义 _app.js 读取 slug、getInitialProps 并将这些道具传递给每个组件,包括所有页面?

使用 NextJS 用 Class Components 和 getInitialProps 做 s-s-r,render 方法有未定义的数据