nextjs渲染篇

本文详细解释了Next.js中的服务器组件工作原理,包括其呈现方式、渲染策略(静态、动态和流式),以及客户端组件的使用和组合模式,如如何区分服务器与客户端组件,上下文提供和交错组件的注意事项。
摘要由CSDN通过智能技术生成

1 服务器组件

默认情况下,Next.js 使用服务器组件。

1.1 服务器组件是如何呈现的?

在服务器上,Next.js 使用 React 的 API 来编排渲染。渲染工作被拆分为多个块:按单个路段和Suspense

每个区块分两个步骤呈现:

  1. React 将服务器组件渲染为一种称为 React Server 组件有效负载 (RSC Payload) 的特殊数据格式。
  2. Next.js 使用 RSC 有效负载和客户端组件 JavaScript 指令在服务器上呈现 HTML

然后,在客户端上:

  1. HTML 用于立即显示路由的快速非交互式预览 - 这仅适用于初始页面加载。
  2. React Server 组件有效负载用于协调客户端和服务器组件树,并更新 DOM。
  3. JavaScript 指令用于 hydrate客户端组件,并使应用程序具有交互性。

RSC 有效负载是渲染的 React Server 组件树的紧凑二进制表示。客户端上的 React 使用它来更新浏览器的 DOM。

 1.2 渲染策略

1.2.1 静态渲染(默认)

使用静态渲染时,路由在构建时渲染,或在数据重新验证后在后台渲染。结果被缓存,

1.2.2 动态渲染

当路由包含对用户个性化的数据或具有仅在请求时才能知道的信息(例如 cookie 或 URL 的搜索参数)

在渲染过程中,如果发现动态函数或未缓存的数据请求,Next.js将切换到动态渲染整个路由。下表总结了动态函数和数据缓存如何影响路由是静态呈现还是动态呈现:

动态函数数据路线
缓存静态渲染
是的缓存动态渲染
未缓存动态渲染
是的未缓存动态渲染

注:静态渲染只有在非动态函数和数据缓存同时存在情况 

1.2.3 流式

通过流式处理,可以从服务器逐步呈现 UI。工作被拆分为多个块,并在准备就绪时流式传输到客户端。这允许用户在整个内容完成呈现之前立即看到页面的某些部分。

 例如:

<Suspense fallback={<Loading />}>
  <Albums />
</Suspense>

children:要呈现的实际 UI。

fallback:如果实际 UI 尚未完成加载,则要代替实际 UI 进行渲染的备用 UI 

2 客户端组件

"use client"用于声明服务器和客户端组件模块之间的边界
例如:

'use client'
 
import { useState } from 'react'
 
export default function Counter() {
  const [count, setCount] = useState(0)
 
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}

 3 组合模式

何时使用服务器和客户端组件?

3.1 将仅限服务器的代码排除在客户端环境之外

npm install server-only

 然后导入server-only

import 'server-only'
 
export async function getData() {
  const res = await fetch('https://external-service.com/data', {
    headers: {
      authorization: process.env.API_KEY,
    },
  })
 
  return res.json()
}

 3.2 使用第三方软件包和提供程序

import { Carousel } from 'acme-carousel'
 
export default function Page() {
  return (
    <div>
      <p>View pictures</p>
 
      {/* Error: `useState` can not be used within Server Components */}
      <Carousel />
    </div>
  )
}

由于三方组件使用了客户端组件,服务器组件不能使用useState

则需要包装一层:

'use client'
 
import { Carousel } from 'acme-carousel'
 
export default Carousel

现在,可以直接在服务器组件中使用:<Carousel />

import Carousel from './carousel'
 
export default function Page() {
  return (
    <div>
      <p>View pictures</p>
 
      {/*  Works, since Carousel is a Client Component */}
      <Carousel />
    </div>
  )
}

3.3 使用上下文提供程序

上下文提供者通常在应用的根附近渲染,以共享全局关注点,例如当前主题。

(1)错误例子:

import { createContext } from 'react'

//  createContext is not supported in Server Components
export const ThemeContext = createContext({})

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
      </body>
    </html>
  )
}

(2)正确例子:(由于服务器组件不支持 React 上下文,采用children的方式)

app/theme-provider.tsx

'use client'
 
import { createContext } from 'react'
 
export const ThemeContext = createContext({})
 
export default function ThemeProvider({
  children,
}: {
  children: React.ReactNode
}) {
  return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
}

 app/layout.tsx

import ThemeProvider from './theme-provider'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  )
}

3.4 交错的服务器和客户端组件

不支持的模式:将服务器组件导入客户端组件

'use client'
 
// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'
 
export default function ClientComponent({
  children,
}: {
  children: React.ReactNode
}) {
  const [count, setCount] = useState(0)
 
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
 
      <ServerComponent />
    </>
  )
}

支持的模式:将服务器组件作为道具传递给客户端组件

'use client'
 
import { useState } from 'react'
 
export default function ClientComponent({
  children,
}: {
  children: React.ReactNode
}) {
  const [count, setCount] = useState(0)
 
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      {children}
    </>
  )
}

 将服务器组件作为子项导入

// This pattern works:
// You can pass a Server Component as a child or prop of a
// Client Component.
import ClientComponent from './client-component'
import ServerComponent from './server-component'
 
// Pages in Next.js are Server Components by default
export default function Page() {
  return (
    <ClientComponent>
      <ServerComponent />
    </ClientComponent>
  )
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流光影下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值