深入浅出:React 18服务器端渲染

深入浅出:React 18服务器端渲染

React 18 引入了大量 SSR 性能改进,这些改进大多是在幕后进行的,尤其是针对非框架用户提供了一些可选选项。其中最有趣的想法是通过服务器端渲染来提高性能,本文将向您全面介绍。

服务器端呈现(SSR)是一种能让 React 应用程序在服务器上而不是在浏览器中呈现的技术。这不仅能提高性能,还能改善用户体验。它包括在服务器上生成 HTML 并将其显示给客户端,而 JavaScript 则处理交互。

如果没有 SSR,就会出现客户端呈现,即浏览器获取并呈现 HTML。当索引不能正确处理 JavaScript 时,这种策略可能有助于搜索引擎优化(SEO)。在通过缓慢的网络下载庞大的 JavaScript 包时,这种策略也很有用。

客户端渲染(CSR)

客户端渲染(CSR)是指在客户端(即用户的网络浏览器)渲染网页。服务器仅提供原始数据或内容,客户端 JavaScript 利用这些数据或内容动态构建最终呈现的页面。

客户端渲染(CSR)

服务器不发送完整的 HTML 内容,而是发送带有 JS 文件的最小 HTML。初次加载可能会比较慢,但随后的加载速度会更快,而且每条路径都不会出现新的 HTML。

客户端渲染网站独立处理逻辑和数据检索。每个页面和 URL 都是动态构建的,因此页面在代码执行后即可使用。

当用户请求页面时,服务器会发送初始 HTML 和所需的 JavaScript 文件。客户端在必要时使用 JavaScript 更新页面,从而避免了全页面重新加载。

以下是 CSR 流程:

  • 用户在地址栏中输入要访问的 URL。
  • 通过 URL 向服务器发送数据请求。
  • 服务器会在第一次请求网站时向客户端浏览器发送静态文件(CSS 和 HTML)。
  • HTML 首先加载,然后是 JavaScript。HTML 文件连接到 JavaScript,显示开发人员定义的加载符号。用户看不到网站。
  • 下载 JavaScript 后,素材会在客户端浏览器上动态生成。
  • 当客户浏览网站并与之互动时,网站内容就会显现出来。

CSR 的优势

  1. 由于采用全页面刷新,应用程序的响应速度更快,用户在页面导航之间看不到 Flash。
  2. 向服务器发送的 HTTP 请求更少,因为每次页面加载时都不会再次下载相同的资产。
  3. 客户端和服务器之间有明确的分工;您可以为不同的平台(如移动、聊天机器人、智能手表)快速构建额外的客户端,而无需修改服务器代码。只要不违反 API 合同,就可以独立修改客户端和服务器上的技术栈。

CSR 的缺点

  1. 由于需要加载许多页面所需的框架、应用程序代码和资产,因此初始页面加载时间会增加。
  2. 在服务器上,你还必须将其设置为将所有请求路由到一个入口点,并允许客户端路由从那里接管。
  3. 在大多数情况下,需要使用外部库。
  4. 在抓取过程中,所有搜索引擎都会执行 JavaScript,它们可能会检测到页面上的空内容。这会在不知不觉中损害应用程序的搜索引擎优化 (SEO)。

何时应使用客户端渲染?

  • 应用程序的用户界面非常复杂,页面和功能众多。
  • 应用程序拥有大量动态数据。
  • 网站的首要任务是编写而不是阅读。
  • 重点是拥有大量用户的丰富网站。

服务器端渲染(SSR)

服务器端渲染(SSR)是指先在服务器上进行网页渲染,然后再发送到客户端的网络浏览器。这种方法不是传输原始数据和依赖客户端,而是由服务器为网页生成最终的 HTML 标记并发送给客户端。

服务器端渲染(SSR)

网络浏览器向服务器发送信息请求,检索用户特定数据以填充并向客户端发送完全渲染的 HTML 页面。每次用户访问网站上的新页面时,服务器都会重复整个过程。

下面将详细介绍 SSR 流程:

  • 用户在地址栏中输入要访问的 URL。
  • 服务器向浏览器发送准备好渲染的 HTML 响应。
  • 浏览器显示页面(现在可以查看)并下载 JavaScript。
  • 浏览器运行 React,使页面具有交互性。

SSR 的优势

  1. 由于需要渲染的代码较少,网站首页的加载速度更快。
  2. 是简单和静态网站的理想选择。
  3. 提高搜索引擎优化,搜索引擎可以抓取网站。

SSR 的缺点

  1. 与传统的客户端内容呈现方式相比,SSR 需要更多的基础设施和开发工作。
  2. SSR 需要为服务器端呈现和浏览器端脚本编写单独的代码层,从而增加了程序的复杂性。
  3. 用户数量过多会造成瓶颈。

何时应使用服务器端渲染?

  • 应用程序的用户界面简单,页面/功能较少。
  • 应用程序中的动态数据较少。
  • 网站倾向于读取而不是写入。
  • 重点不在内容丰富的网站,用户较少。

客户端渲染与服务器端渲染的主要区别

  • 性能:客户端渲染会因 JavaScript 的获取和执行而减慢初始加载时间。服务器端渲染可生成完全渲染的 HTML,通常可加快加载速度并带来更流畅的用户体验,尤其是在速度较慢的网络或设备上。
  • 搜索引擎优化:另一个区别是搜索引擎优化。客户端呈现会妨碍搜索引擎的抓取和索引,因为它依赖于 JavaScript。而服务器端呈现可使内容随时可供适当索引。
  • 开发设置:客户端呈现更容易上手,所需的服务器端代码也更少,但随着应用程序的增长,维护起来可能会变得更具挑战性。服务器端呈现需要更多的服务器端代码,实施起来可能更棘手,但它能提供更好的用户体验和搜索引擎优化优势。

使用 React 18 实现服务器端渲染

第 1 步:使用 create-react-app 命令行工具创建一个新的 React 应用程序。打开首选终端,键入以下命令。

npx create-react-app server-api-demo-app

第 2 步:切换到新创建的 React 应用程序。

cd server-api-demo-app

第 3 步:要管理路由,在项目中添加 react-router-dom

npm install react-router-dom

第 4 步:在应用程序中包含一些页面。您可以在 app.js 中包含以下示例路由: (i) Homepage (ii) About

const App = () => (
<div>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
</div>
);

第 5 步:在两页空白处填写内容。点击 [此处] 以供参考。

第 6 步:在根目录下新建一个名为 server 的文件夹,然后添加 index.jsserver.js 文件。将下面的代码复制并粘贴到该文件中。

// server/index.js
require("ignore-styles");
require("@babel/register")({
ignore: [/(node_modules)/],
presets: ["@babel/preset-env", "@babel/preset-react"],
});
require("./server");

该代码段配置了用于代码翻译的 Babel,排除了特定文件(如 “node_modules” 中的文件),并通过导入 “server” 模块启动服务器。在 React 服务器端呈现中,该参数通常用于允许服务器处理和提供 React 组件

// server/server.js
import express from "express";
import React from "react";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom/server";
import App from "../src/App";
const app = express();
app.get("/*", (req, res) => {
const entryPoint = ["/main.js"];
const { pipe, abort: _abort } = ReactDOMServer.renderToPipeableStream(
<StaticRouter location={req.url}>
<App />
</StaticRouter>,
{
bootstrapScripts: entryPoint,
onShellReady() {
res.statusCode = 200;
res.setHeader("Content-type", "text/html");
pipe(res);
},
onShellError() {
res.statusCode = 500;
res.send("<!doctype html><p>Loading...</p>");
},
}
);
});
app.listen(3002, () => {
console.log("App is running on http://localhost:3002");
});

使用 app.get("/*",...),代码会为所有路由创建一个路由处理程序。这表明该路由处理程序将处理任何传入的服务器请求。在路由处理程序中:

 

  •  main.js 的值被分配给 entryPoint 数组。这表示用于引导客户端代码的 JavaScript 文件。
  •  ReactDOMServer.renderToPipeableStream() 接收两个参数:一个用于 HTML 渲染的 React Node 和一个包含流参数(可选)的 options 对象。它会返回一个包含两个方法的对象:pipe 和 abort。 pipe 方法将 HTML 写入给定的 Node.js 流。为了允许流式传输,我们在 onShellReady 中使用 pipe。onAllReady 也可用于静态生成和爬虫。
  • 当渲染过程结束,HTML 准备好向客户端传输时,就会调用 onShellReady() 函数。它将响应状态代码设置为 200,将内容类型头定义为 text/html ,然后使用管道函数将渲染的 HTML 管道到响应中。
  • 当渲染过程中出现错误时,将执行 onShellError() 回调函数。它会将响应状态代码设置为 500,并发送一条 HTML 编码的简单错误信息。

第 7 步:在客户端,我们需要用 index.js 文件中的 ReeactDOM.hydrareRoot 更新 ReactDOM.createRoot ,以使服务器生成的 HTML 具有交互性。

// index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
ReactDOM.hydrateRoot(
document,
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);

第 8 步:要在服务器上运行代码,请在 package.json 文件中包含以下脚本。

"ssr": "npm run build && node server/index.js"

该命令将构建项目,在服务器上执行代码,并将结果输出到 localhost:3002

第 9 步:使用 npm run ssr 命令查看输出结果。

使用 npm run ssr 命令

React 服务器组件(RSC)

React 服务器组件(RSC)将数据抓取和远程客户端-服务器交互纳入框架,从而扩展了 React 的基础,使其不仅仅是一个渲染库。

React 的设计宗旨是模块化和逐步集成到现有代码库中。为了满足世界对丰富交互性的渴望,它将客户端和服务器分离开来,使前端的构建更加灵活。这一点对团队尤为重要:由不同开发人员创建的两个 React 组件只有在共享相同基础的情况下才能一起运行。

为了实现这一目标,React 必须在已有的 Web 标准基础上进行创新。在过去十年多页面应用程序(MPA)和单页面应用程序(SPA)、客户端和服务器端呈现之间的演变过程中,我们的目标始终如一:提供快速数据、丰富的交互性,并保持出色的开发人员体验。

RSC 完全在服务器上获取数据并进行渲染,然后将生成的 HTML 流式传输到客户端的 React 组件树中,并根据需要与其他服务器和客户端组件交错使用。

这种方法避免了客户端重新渲染的要求,从而提高了性能。由于计算负载由客户端和服务器分担,因此水合可与任何客户端组件流入的 RSC 同时进行。

小结

有了新的服务器 API,React 组件现在可以使用 Node.js 或 Web 流渲染成服务器渲染的 HTML。一些框架(如 Next.js、Remix 和 Gatsby)可以自动完成这一过程,从而提高初始加载时间、搜索引擎优化、用户体验和抵御 XSS 攻击的安全性。

SSR 虽然具有优势,但也会带来复杂性和服务器负载的增加,可能不适合聊天或多人游戏等实时应用。请评估您的需求,确保 SSR 符合您的需要。

评论留言