大数据

next js Rendering 学习笔记 01

当一个用户访问网站时,服务器会发送回一个单一的HTML页面

一旦浏览器下载了所有的JavaScript,它就会开始在计算机上生成HTML并将其注入到DOM根节点下,这时可以看到最终界面

Client-side Rending (CSR)

浏览器作为一个客户端,将React组件转换为你在屏幕上看到的内容,也就是客户端渲染

这种形式期初 加载div空页面然后js渲染页面内容 对SEO不友好,有意义的内容可能加载太慢,搜索引擎无法捕捉都它。

获取数据 渲染页面都在客户端执行是一个繁重的工作

Server-side Rending (SSR)

HTML在服务器上生成,浏览器可以快速解析并显式它,从而给我们更快的初始页面加载时间。

  • 解决了SEO问题,用户看到实际的HTML内容,而不是空白屏幕或加载屏幕

该页面只有在JavaScript包下载并执行完毕后才能完全交互,这个包包括react本身以及你应用程序的代码

这个中药的阶段被成为hydration,在此期间,最初由服务器提供的静态HTML页面被激活

在hydration过程中react在浏览器中接管,并使用服务器渲染的HTML作为蓝图,在内存中重构组件,它仔细的规划了所有交互元素的位置,然后将JavaScript逻辑连接起来。

比如初始化应用程序状态、添加点击和鼠标悬停处理程序以及设置所有动态功能,以实现完整的交互用户体验。

存在问题:

  • 如果一个组件需要从数据库或其他来源(如API)获取数据,这个获取过程必须在服务器开始渲染页面之前完成。这可能会延迟服务响应时间给给浏览器
  • 为了成功进行hydration,进行hydration之前,所有的JavaScript代码都必须加载到客户端
  • react会一次性将组件书进行hydration 也就是一旦开始hydration,他不会停止,直到整个树都完成。

为了弥补SSR以上的性能缺陷 React 18引用了 Suspense SSR架构

HTML流式加载 ,内容包裹在Suspense中,被包裹的部分会等待内容加载,但是不影响其他部分使用

如果有多个内容,则当前用户交互的区域会首先加载,比如sidenav也是suspense的 浏览页面的时候用户点了maincontent区域则优先加载maincontent区域内容

React的渲染,从客户端渲染发展到服务端渲染再到用于服务端渲染的Suspense

CSR -> SSR -> Suspense for SSR

React Server Components (RSC)

客户端组件

  • 通常在客户端渲染,但也可以在服务器上渲染一次为HTML,使用户可以立即看到页面的HTML内容而不是空白屏幕
  • 客户端组件可以完全访问客户端环境:use state, effects, event listener
  • 需添加use client标识

服务端组件

  • 代码保留在服务器上,永远不会下载到客户端
  • 可以直接访问服务器资源
  • 不需要下载、解析和执行JavaScript,没有hydration使你的应用加载变得交互更快
  • 可以直连数据库
  • 敏感数据在服务端不会外漏
  • 服务器渲染会缓存结果
  • SEO提升

因为使用一种特殊的JSON格式而不是HTML,React可以在保持重要UI状态完整的同时更新所有内容。

Static rendering

在服务端的渲染策略生成HTML页面,也就是HTML静态页面缓存

一个页面生成 .html .meta .rsc三个文件

页面中的导航链接页面会跟随一起预加载(prefetching),访问导航链接页面也就不会再次请求服务器

也就是连同HTML一起生成的还有RSC payloads和客户端hydration所需的JavaScript代码块,这样也可以预加载导航链接的页面 因为对应的RSC payloads和JavaScript代码块已经被预加载了,无需额外的服务器请求。

Dynamic rendering

nextjs检测到我们使用动态函数或者动态API时会自动切换到动态渲染

动态函数:

  • cookies()
  • headers()
  • connection()
  • drafMode()
  • searchParams prop
  • after()

打包的时候会输出 哪些路由是静态渲染 哪些是动态渲染

generateStaticParams 函数

  • 与动态路由协同工作
  • 在build时生成静态路由,而不是在请求时按需生成

使用[id]动态传参的页面路由 是动态渲染,因为下一个渲染id是不知道的,只有访问页面传参的时候才知道

上面产品详情页在请求时按需渲染

export async function generateStaticParams() {
  return [{ id: "1" }, { id: "2" }, { id: "3" }];
}

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  return <h1>Product {id} details</h1>;
}

以上generateStaticParams函数可以在build时候预先生成对应id页面HTML

嵌套动态参数路由

可以设置 dynamicParams = false 这样除了预生成的路由之外 访问其他动态参数页面 返回404页面

streaming 流式加载

配合suspense使用

// 组件
export const Product = async () => {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  return <div>Product</div>;
};

// 组件
export const Reviews = async () => {
  await new Promise((resolve) => setTimeout(resolve, 4000));
  return <div>Reviews</div>;
};

// 页面
import { Suspense } from "react";

import { Product } from "@/components/product";
import { Reviews } from "@/components/reviews";

export default function ProductDetailPage() {
  return (
    <div>
      <h1>Product detail page</h1>
      <Suspense fallback={<p>Loading product details...</p>}>
        <Product />
      </Suspense>
      <Suspense fallback={<p>Loading reviews...</p>}>
        <Reviews />
      </Suspense>
    </div>
  );
}

server-only包 在build时候即可报错

npm i server-only

使用方式

import "server-only"

Context providers

共享全局状态和逻辑

"use client";
import { createContext, useContext } from "react";

type Theme = {
  colors: {
    primary: string;
    secondary: string;
  };
};

const defaultTheme: Theme = {
  colors: {
    primary: "#007bff",
    secondary: "#6c757d",
  },
};

const ThemeContext = createContext<Theme>(defaultTheme);

export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
  return (
    <ThemeContext.Provider value={defaultTheme}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useTheme = () => useContext(ThemeContext);

layout.tsx使用ThemeProvider

<html lang="en">
  <ThemeProvider>
    <body
      className={`${geistSans.variable} ${geistMono.variable} antialiased`}
    >
      {children}
    </body>
  </ThemeProvider>
</html>

client-only

npm i client-only

使用方式

import "client-only"

server components可以嵌套

client components可以嵌套

client component可以嵌套在server component中

server component 不能嵌套 在client component 中因为任何嵌套在客户端组件内的组件都会自动成为客户端组件

可以将server component作为子属性传递给客户端组件

client component修改如下:

"use client"

import { useState } from "react"

export const ClientCompoentOne = ({
    children,
}: {
    children: React.ReactNode
}) => {
    const [name, setName] = useState("Batman")
    return (
        <>
        <h1>Client component one</h1>
        {children}
        </>
    )
}

使用方式

import { ClientCompoentOne } from "@/components/client-component-one";
import { ServerComponentOne } from "@/components/server-component-one";

export default function InterLeavingPage() {
    return (
        <>
            <h1>Interleaving page</h1>
            <ClientCompoentOne>
                <ServerComponentOne />
            </ClientCompoentOne>
        </>
    )
}