理解React应用程序中的Hydration

1、一些API

在 React 世界中,我们遇到了不同的API,如服务器端渲染(SSR)、客户端渲染(CSR)、ReactDOM、ReactDOMServer 等,我们可能并不完全了解它们。 让我们快速了解这些API。

ReactDOM

ReactDOM 是一个提供 DOM 特定方法的包,可用于 Web 应用的最顶层。它提供了一种高效管理网页 DOM 组件的方法。 render()和hydrate()函数是 react-dom 包的模块。

render()

ReactDOM.render(element, container[, callback])

render() 函数 是 ReactDOM 最有用的函数之一。它将 React 元素渲染到提供的容器中的 DOM 中后返回对组件的引用(对于无状态组件则返回 null)。

hydrate()

ReactDOM.hydrate(element, container[, callback])

hydrate()与 render() 相同,但用于填充由 ReactDOMServer 渲染的 HTML 内容的容器。React 将尝试将事件监听器附加到现有标记。

ReactDOMServer

ReactDOMServer 对象 使我们能够将组件渲染为静态标记

ReactDOMServer 的renderToString() renderToStaticMarkup() 函数可以在服务器和浏览器中使用。

2、客户端渲染(CSR)

客户端渲染 (CSR) 是指使用 JavaScript 直接在浏览器中渲染页面。所有逻辑、数据获取、模板和路由均在客户端处理。

当浏览器收到页面请求时,它会发送 HTML、CSS 和 JS 代码以在浏览器中运行。脚本标签包含 React 代码中的所有指令。它会在浏览器中加载,然后应用程序就会变为可交互的。

如果网络速度慢或包大小较大,CSR 可能需要一些时间才能让用户看到网站。在这种情况下,用户通常会看到空白页。这会给用户体验带来不良影响。它还会影响 SEO,因为网络爬虫无法索引空白网站。

图片取自 Shaundai 在 React Conf 2021上的演讲。

3、服务器端渲染 (SSR)

为了理解 SSR,我们来类比一下餐厅里供应的食物。假设我们点了一份开胃菜、印度煎饼、扁豆、咖喱等。订单需要很长时间才能送达。我们会很沮丧。饥饿的顾客甚至可能会对服务员大喊大叫。 但是,如果我们坐下时有人给我们送来一些薄脆饼干、沙拉或者迎宾饮料怎么办?

太棒了!至少我们有一个开始!

SSR 中也采用了类似的方法。 在服务器端渲染中,当用户请求网页时,服务器会通过获取用户特定的数据来准备 HTML 页面并将其发送到用户的机器。然后浏览器构建内容并显示页面。从数据库获取数据、创建 HTML 页面并将其发送到客户端的整个过程只需几毫秒即可完成。

在这个过程中,用户可以看到浏览器上的内容,而不是一片空白,让用户感到开心,提升了用户体验。

图片取自 Shaundai 在 React Conf 2021上的演讲。 现在,让我们研究一下 React 应用程序中的 Hydration

React hydration

React hydration 是一种与渲染类似的技术,但我们不打算使用空 DOM 来将所有 React 组件渲染进去,而是使用已经构建好的 DOM,其中所有组件都渲染为 HTML。

基本 React 应用程序:

const root = document.querySelector("#root");
ReactDOM.render(<App name="Saeloun" />, root);

在客户端加载脚本之前应用程序的输出:

<html>
  <head></head>
  <body>
    <div id="root"></div>
  </body>
</html>
SSR应用:
// index.js


ReactDOM.hydrate(<App name="Saeloun"/>, document.getElementById('root'));


//server.js


import React from "react";
import ReactDOMServer from "react-dom/server";


app.use("/", (req, res, next) => {
  fs.readFile(path.resolve("./build/index.html"), "utf-8", (err, data) => {
    if (err) {
      console.log(err);
      return res.status(500).send("Some error happened");
    }
    return res.send(ReactDOMServer.renderToString(<App name="Saeloun" />)
    )
  });
});


//App.js


import React from "react";


function App(props) {
  return (
    <div>
      Hello {props.name}!
    </div>
  )
}


export default App;

在客户端加载脚本之前应用程序的输出:

<html>
  <head></head>
  <body>
    <div id="root">
      <h1>Hello Saeloun!</h1>
    </div>
  </body>
</html>

现在,考虑一下SSR 讨论中提到的一个例子 。 我们希望在应用程序准备就绪时显示一个完全可交互的应用程序。

绿色表示页面的这些部分是交互的,即所有事件处理程序都已附加( JS 已完全加载)。 在 CSR 中,JavaScript 加载时用户唯一看到的是一个空白页,从而降低了用户体验。

在 SSR 中,我们将服务器上的 React 组件渲染为 HTML 并将其发送给用户。HTML 的交互性不是很好。但是,它可以让用户在 JavaScript 仍在加载时看到一些内容:

此处,灰色表示屏幕的这些部分尚未完全交互。JavaScript 代码尚未加载,因此单击按钮不会执行任何操作。 我们告诉 React 将事件处理程序附加到 HTML,以使应用程序具有交互性。渲染组件和附加事件处理程序的过程称为“水合”。这就像用交互性和事件处理程序的“水”浇灌“干”的 HTML。水合后,我们的应用程序变得具有交互性,可以响应点击等。

React 期望服务器和客户端之间渲染的内容是相同的。它可以修补文本内容的差异。不匹配必须视为错误并应予以修复。在开发模式下,React 会在 hydration 期间警告不匹配。

如果某个元素的属性或文本内容在服务器和客户端之间不可避免地存在差异(例如时间戳),我们可以通过suppressHydrationWarning={true}向元素添加来消除警告。但是,不应过度使用!

如果我们有意需要在服务器和客户端上渲染不同的东西,我们可以进行两遍渲染。在客户端上渲染不同内容的组件可以读取状态变量,例如this.state.isClient,可以在中将其设置为 true componentDidMount()。它将渲染与服务器中相同的内容,避免不匹配。但是,在 hydration 之后会同步进行额外的渲染。这种方法会使我们的应用程序变慢,因此应谨慎使用。

4、总结

总结一下:使用 Hydration 的好处 改善搜索引擎优化 减少初始加载时间



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部