使用Next.js App Router进行动态客户端渲染的服务器端渲染

huangapple go评论68阅读模式
英文:

Server side rendering with dynamic client side using nextjs App Router

问题

以下是您要翻译的内容:

使用NextJs,我经常使用的一种模式是在服务器端渲染一切(包括数据获取),然后过渡到动态客户端渲染。这是一个刻意的示例:

export async function getServerSideProps() {
  const res = await fetch(`api/myData`)
  const data = await res.json()

  return {
    props: { initialData: [data] }
  }
}

export default function About({ initialData }) {
  const [data, setData] = useState(initialData)

  const addItem = () => {
    const item = "foo"
    const res = await fetch('/api/addData')
    setData([...data, JSON.parse(res.body).item])
  }

  return (
    <div>
      {data.map(item => (<div>{item}</div>))}

      <button onClick={addItem}>Add Item</button>
    </div>
  )
}

我无法弄清如何在使用NextJs的新App Router构建样式中获得相同类型的功能。有人能向我展示我漏掉了什么吗?

英文:

A pattern that I've used a ton with NextJs is to render everything server side (including data fetching) and then transition to dynamic client side rendering. Contrived example:

export async function getServerSideProps() {
  const res = await fetch(`api/myData`)
  const data = await res.json()

  return {
    props: { initialData: [data] }
  }
}

export default function About({ initialData }) {
  const [data, setData] = useState(initialData)

  const addItem = () =&gt; {
    const item = &quot;foo&quot;
    const res = await fetch(&#39;/api/addData&#39;)
    setData([...data, JSON.parse(res.body).item])
  }

  return (
    &lt;div&gt;
      {data.map(item =&gt; (&lt;div&gt;{item}&lt;/div&gt;))}
      
      &lt;button onClick={addItem}&gt;Add Item&lt;/button&gt;
    &lt;/div&gt;
  )
}

And I cannot figure out how I can get the same sort of functionality with the new App Router style of building NextJs. Can anyone show me what I am missing?

答案1

得分: 5

Sure, here is the translated content:

  1. 按步骤来解释:

    1. 你想要在服务器端动态获取initialData,这意味着你需要在服务器组件中获取数据,我认为page.tsx组件是一个很好的地方来实现这个,所以一个示例的页面组件如下:

      export default async function Page() {
        const res = await fetch(`api/myData`, {cache: 'no-store'});
        const data = await res.json();
      
        return <About initialData={[data]} />
      }
      

      请注意,fetch 中有 cache: 'no-store',这是为了选择动态渲染,因为静态渲染是默认的,也可以使用 revalidate: 0 来实现。

    2. 你需要将<About />变成客户端组件,以便能够使用useState等功能。所以,在你的About组件文件的顶部需要添加 use client

  2. 需要记住的一件事是,在实际的应用中,你可能希望你的页面是静态渲染的。一个解决方案是将我们所做的封装到一个单独的服务器组件中,让该服务器组件获取数据并进行动态渲染,而不是整个页面组件。

此外,关于客户端组件和服务器组件之间的动态关系。客户端组件实际上是在服务器端(HTML/CSS和一个小型JS包)预渲染的,但它们还需要一个不能在服务器上运行的JS有效载荷,如果你考虑一下,将useState在服务器上运行没有意义,因为状态存在于用户浏览器中,甚至在服务器上拥有onClick监听器也没有意义。当Next.js预渲染客户端组件时,它返回渲染的HTML/CSS/服务器JS以及需要在客户端执行的客户端JS有效载荷。另一方面,服务器组件不需要客户端JS,因此只从服务器返回HTML/CSS和一小段JS。在你的原始示例中,getServerSideProps 在服务器上执行,但<About />组件具有客户端有效载荷,就像使用新的应用程序文件路由的组件一样。当前不支持在服务器上完全渲染具有客户端代码(如useStateonClick)的组件,这也没有意义。至于HTML和CSS,如我之前所说,它们最初在服务器上为客户端组件进行预渲染。你可以在这里阅读更多信息:https://nextjs.org/docs/getting-started/react-essentials#client-components

英文:

Let's break this down by steps:

  1. You want to fetch the initalData server side dynamically, that means that you need to fetch your data in a server component, I think the page.tsx component is a pretty good place to achieve this, so an example page component would be:

    export default async function Page() {
      const res = await fetch(`api/myData`, {cache: &#39;no-store&#39;});
      const data = await res.json();
    
      return &lt;About initialData={[data]} /&gt;
    }
    

    note that fetch has cache: &#39;no-store&#39;, this is needed in order to opt into dynamic rendering since static rendering is the default, this could also be achieved with revalidate: 0.

  2. You need to make &lt;About /&gt; a client component in order to be able to use useState etc. So you need to add &#39;use client&#39; at the top of your About component file.

One thing to keep in mind is that in a real world application you may want your page to be statically rendered, a solution is to encapsulate what we did into a separate server component and have that server component to fetch the data and dynamically render instead of the whole page component.

Also regarding the dynamic between client and server components. Client components are in fact prerendered in the server (html/css and a small js bundle), but they also require a js payload that cannot run in the server, if you think about it, it makes no sense to run useState in the server since the state lives in the user browser, even less to have an onClick listener in the server. When nextjs prerenders a client component it sends back the rendered html/css/server js and the client js payload that need to be executed in the client. Server components in the other hand doesn't require that client js and so only html/css and a small js is returned from the server. In your original example getServerSideProps is executed in the server but the &lt;About /&gt; component has a client payload just like a component with use client with the new app folder router. Having a component with client code like useState and onClick rendered 100% in the server is currently not supported and makes zero sense. Regarding html and css as I said before, it is initially prerendered in the server for clients component as well. You can read more about it here https://nextjs.org/docs/getting-started/react-essentials#client-components

huangapple
  • 本文由 发表于 2023年5月7日 19:04:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76193533.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定