目录

React 服务端流式渲染

渲染模式

我们可以通过服务器端渲染来减少交互时间。当需要生成一个导航文件,我们可以将其拆分为更小的块!Node流允许我们将数据流式传输到响应对象,这意味着我们可以持续向客户端发送数据。客户端一旦接收到数据块,就可以开始渲染内容。

此处为语雀视频卡片,点击链接查看:ssr-1.webm

React的内置renderToNodeStream方法使我们能够将代码以小块的形式发送。由于客户端可以在接收数据的同时开始绘制UI,我们可以非常快速的加载首页。

此处为语雀视频卡片,点击链接查看:ssr-2.webm

假设我们有一个应用程序,在App组件中向用户展示数千条关于猫的信息!

jsx 复制代码
import React from "react";
import path from "path";
import express from "express";
import { renderToNodeStream } from "react-dom/server";

import App from "./src/App";

const app = express();

app.get("/favicon.ico", (req, res) => res.end());
app.use("/client.js", (req, res) => res.redirect("/build/client.js"));

const DELAY = 500;
app.use((req, res, next) => {
  setTimeout(() => {
    next();
  }, DELAY);
});

const BEFORE = `
<!DOCTYPE html>
  <html>
    <head>
      <title>Cat Facts</title>
      <link rel="stylesheet" href="/style.css">
      <script type="module" defer src="/build/client.js"></script>
    </head>
    <body>
      <h1>Stream Rendered Cat Facts!</h1>
      <div id="approot">
`.replace(/\n\s*/g, "");

app.get("/", async (request, response) => {
  try {
    const stream = renderToNodeStream(<App />);
    const start = Date.now();

    stream.on("data", function handleData() {
      console.log("Render Start: ", Date.now() - start);
      stream.off("data", handleData);
      response.useChunkedEncodingByDefault = true;
      response.writeHead(200, {
        "content-type": "text/html",
        "content-transfer-encoding": "chunked",
        "x-content-type-options": "nosniff"
      });
      response.write(BEFORE);
      response.flushHeaders();
    });
    await new Promise((resolve, reject) => {
      stream.on("error", err => {
        stream.unpipe(response);
        reject(err);
      });
      stream.on("end", () => {
        console.log("Render End: ", Date.now() - start);
        response.write("</div></body></html>");
        response.end();
        resolve();
      });
      stream.pipe(
        response,
        { end: false }
      );
    });
  } catch (err) {
    response.writeHead(500, {
      "content-type": "text/pain"
    });
    response.end(String((err && err.stack) || err));
    return;
  }
});

app.use(express.static(path.resolve(__dirname, "src")));
app.use("/build", express.static(path.resolve(__dirname, "build")));

const listener = app.listen(process.env.PORT || 2048, () => {
  console.log("Your app is listening on port " + listener.address().port);
});

codesandbox地址

App组件使用内置的renderToNodeStream方法进行流式渲染。

html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <title>Cat Facts</title>
    <link rel="stylesheet" href="/style.css" />
    <script type="module" defer src="/build/client.js"></script>
  </head>
  <body>
    <h1>Stream Rendered Cat Facts!</h1>
    <div id="approot"></div>
  </body>
</html>

数据包含应用程序渲染所需的信息,例如文档标题和样式表。如果我们使用renderToString方法在服务器端渲染App组件,我们将需要等待应用程序接收所有数据后,才能处理这些元数据。为了加快速度,renderToNodeStream使应用程序可以在接收来自App组件的数据块的同时,加载和处理这些信息!

概念

与渐进式水合类似,流式渲染是另一种可以用来提高服务器端渲染(SSR)性能的机制。顾名思义,流式渲染意味着从服务器发送到客户端的HTML是流式传输的。由于客户端可以更早地接收HTML,页面首次字节时间(TTFB)会减少。由于渲染是渐进式的,因此首次绘制(FP)和首次内容绘制(FCP)的时间也会更短。

React的流式渲染支持

React在2016年发布的React 16中引入了对流式渲染的支持。以下API支持流式渲染。

  1. ReactDOMServer.renderToNodeStream(element):
    该函数的输出与ReactDOMServer.renderToString(element)相同,以Node.js可读流格式返回,而不是字符串。该函数仅在服务器端用于流式方式渲染HTML。接收的客户端可以调用ReactDOM.hydrate()来水合页面。
  2. ReactDOMServer.renderToStaticNodeStream(element):
    可用于在服务器端渲染静态、非交互式页面,然后将其流式传输到客户端。

将所有内容整合在一起,现在让我们看看这个代码:

jsx 复制代码
import { renderToNodeStream } from 'react-dom/server';
import Frontend from '../client';

app.use('*', (request, response) => {
  // Send the start of your HTML to the browser
  response.write('<html><head><title>Page</title></head><body><div id="root">');

  // Render your frontend to a stream and pipe it to the response
  const stream = renderToNodeStream(<Frontend />);
  stream.pipe(response, { end: 'false' });
  // Tell the stream not to automatically end the response when the renderer finishes.

  // When React finishes rendering send the rest of your HTML to the browser
  stream.on('end', () => {
    response.end('</div></body></html>');
  });
});

SSR与流式渲染的绘制的对比:

null

来源:mxstbr.com/thoughts/st...

流式SSR的优缺点

流式渲染旨在提高React的服务器端渲染性能,并提供以下好处:

  1. 性能提升:由于第一个字符在服务器端渲染开始后不久即可到达客户端,因此TTFB比SSR更好。由于客户端可以在接收到数据后立即开始解析HTML,因此首次绘制(FP)和首次内容绘制(FCP)的时间也更短。
  2. 处理背压:流式渲染对网络背压或拥塞响应良好。
  3. 支持SEO:流式传输的响应可以被搜索引擎爬虫读取,从而允许网站进行SEO优化。

需要注意的是,流式渲染的实现并不是简单的将renderToString替换为renderToNodeStream()。有些情况下,与SSR工作的代码可能无法直接与流式渲染一起使用。

以下是迁移可能的几个示例。

  1. 在服务器渲染过程中生成需要添加到文档中的框架。例如,动态添加到页面中的CSS的框架,或在渲染时向文档<head>中添加元素的框架。这里讨论了一种解决方法。
  2. 使用renderToStaticMarkup生成页面模板并在其中嵌入renderToString调用以生成动态内容的代码。由于在这种情况下期望组件对应的字符串,因此不能用流替换。这里提供了一个这样的代码示例。
jsx 复制代码
res.write("<!DOCTYPE html>");

res.write(renderToStaticMarkup(
 <html>
   <head>
     <title>My Page</title>
   </head>
   <body>
     <div id="content">
       { renderToString(<MyPage/>) }
     </div>
   </body>
 </html>);

流式渲染和渐进式水合都可以帮助弥合服务器端渲染和客户端渲染之间的差距。

原文:www.patterns.dev/react/strea...

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
OK_boom6 小时前
React状态管理器的应用
前端·javascript·react.js
萌萌哒草头将军9 小时前
🚀🚀🚀Zod 深度解析:TypeScript 运行时类型安全的终极实践指南
javascript·vue.js·react.js
EricXJ14 小时前
useMutation Hook 使用指南
前端·react.js·typescript
小浣熊喜欢揍臭臭18 小时前
手动搭建并配置react项目(webpack5)
前端·react.js·前端框架
阿豪啊18 小时前
React入门(三)-封装 Fetch 以及增加接口模拟请求
react.js
太阳花ˉ1 天前
React(七):Redux
react.js
HaanLen2 天前
React19源码系列之Hooks(useRef)
javascript·react.js
前端大白话2 天前
React 中shouldComponentUpdate生命周期方法的作用,如何利用它优化组件性能?
react.js
凉生阿新2 天前
【React】基于 React+Tailwind 的 EmojiPicker 选择器组件
前端·react.js·前端框架
公子小六2 天前
ASP.NET Core WebApi+React UI开发入门详解
react.js·ui·c#·asp.net·.netcore