110

我已经阅读了文档,但我并没有真正理解React 16hydrate()之间的区别。render()

我知道hydrate()用于结合 SSR 和客户端渲染。

有人可以解释什么是保湿,那么 ReactDOM 有什么区别?

4

7 回答 7

102

ReactDOMServer文档(强调我的):

如果你调用ReactDOM.hydrate()一个已经有这个服务器渲染标记的节点,React 将保留它并且只附加事件处理程序,让你有一个非常高性能的首次加载体验。

粗体字是主要区别。render如果初始 DOM 和当前 DOM 之间存在差异,可能会更改您的节点。hydrate只会附加事件处理程序。

作为单独 API引入的 Github 问题中hydrate

如果这是您的初始 DOM:

<div id="container">
    <div class="spinner">Loading...</div>
</div>

然后调用:

ReactDOM.render(
   <div class="myapp">
      <span>App</span>
   </div>,
   document.getElementById('container')
)

打算只进行客户端渲染(而不是水化)。然后你以

<div id="container">
   <div class="spinner">
       <span>App</span>
   </div>
</div>

因为我们不修补属性。

仅供参考,他们没有修补属性的原因是

...在正常水合模式下水合真的很慢,并且会减慢初始渲染到非 SSR 树的速度。

于 2017-10-01T21:02:39.673 回答
49

我没有任何具体的内容可以添加到上面所说的关于使用的内容hydrate,但在尝试了解它时,我整理了一个小例子,所以这里的工作对任何认为有帮助的人都有帮助。

目标

提供两页,一页使用ReactDOM.hydrate,一页使用ReactDOM.render。它们将依赖于一些用 JSX 编写的 react 组件,这些组件由<script>标签加载,并给予人为延迟(由服务器)来说明hydraterender.

基本结构

  1. 一个具有 HTML“骨架”的文件
  2. 一个包含用 JSX 编写的自定义 React 组件的文件
  3. 一个脚本生成所有页面供服务器使用
  4. 一个脚本来运行服务器

结果

在生成页面并运行服务器后,我会转到127.0.0.1标题hydrate、一个按钮和两个链接。我可以单击按钮,但没有任何反应。片刻之后,文档完成加载,按钮开始计算我的点击次数。然后我点击“渲染”链接。现在,我看到的页面有标题渲染和两个链接,但没有按钮。片刻之后,按钮出现并立即响应。

解释

在“水合物”页面上,所有标记都会立即呈现,因为所有必要的 html 都随页面一起提供。该按钮无响应,因为尚未连接回调。加载components.js完成后,load事件会从 触发,window并且回调与hydrate.

在“渲染”页面上,按钮标记不随页面一起提供,而仅由 注入ReactDOM.render,因此不会立即可见。注意页面的外观是如何被最终加载的脚本改变的。

资源

这是我正在使用的自定义反应组件。它将被节点中的服务器用于对静态渲染组件做出反应,并且还将从服务器动态加载以在页面中使用(这是检查文件开头的对象的目的)exportsReact

// components.jsx

var exports = typeof(exports) == 'object' ? exports : {};
var React = typeof(React) == 'object' ? React : require('react');

function MyButton(props) {
  [click, setClick] = React.useState(0);
  function handleClick() { setClick(click + 1); }
  return (
    <button onClick={handleClick}>Clicked: {click}</button>
  );
}

exports.MyButton = MyButton;

这是用于生成服务器所需的所有页面的脚本。首先,使用 babel 将 components.jsx 转换为 javascript,然后使用这些组件以及 React 和 ReactDOMServer 来创建实际页面。这些页面是使用getPage从文件中导出的功能创建的pageTemplate.js,如下所示。

// genScript.js

let babel          = require('@babel/core');
let fs             = require('fs');
let ReactDOMServer = require('react-dom/server');
let React          = require('react');
let pageTemplate   = require('./pageTemplate.js');

script = babel.transformFileSync(
  'components.jsx', 
  {presets : [['@babel/react']]}
);

fs.writeFileSync('components.js',script.code);
let components = require('./components.js');

hydrateHTML = pageTemplate.getPage(
  'MyButton',
  ReactDOMServer.renderToString(React.createElement(components.MyButton)),
  'hydrate'
);

renderHTML = pageTemplate.getPage(
  'MyButton',
  '',
  'render'
);

fs.writeFileSync('hydrate.html',hydrateHTML);
fs.writeFileSync('render.html',renderHTML);

这个文件只是导出getPage前面提到的函数。

// pageTemplate.js

exports.getPage = function(
  reactElementTag,
  reactElementString,
  reactDOMMethod
  ) {
  return `
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8" />
      <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js" defer></script>
      <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" defer></script>
      <script src="./components.js" defer></script>
    </head>
    <body> 
      <h1>${ reactDOMMethod }</h1>
      <div id="react-root">${ reactElementString }</div> 
      <a href="hydrate.html">hydrate</a>
      <a href="render.html">render</a>
    </body>
    <script>
      window.addEventListener('load', (e) => {
        ReactDOM.${ reactDOMMethod }(
          React.createElement(${ reactElementTag }),
          document.getElementById('react-root')
        );
      });
    </script>
  </html>
  `;
}

最后,实际的服务器

// server.js

let http = require('http');
let fs   = require('fs');

let renderPage       = fs.readFileSync('render.html');
let hydratePage      = fs.readFileSync('hydrate.html');
let componentsSource = fs.readFileSync('components.js');

http.createServer((req, res) => {
  if (req.url == '/components.js') {
    // artificial delay
    setTimeout(() => {
    res.setHeader('Content-Type','text/javascript');
    res.end(componentsSource);
    }, 2000);
  } else if (req.url == '/render.html') {
    res.end(renderPage);
  } else {
    res.end(hydratePage);
  }
}).listen(80,'127.0.0.1');
于 2020-01-30T09:04:02.907 回答
43

Hydrate 主要用于 SSR(服务器端渲染)。SSR 为您提供从服务器发送的骨架或 HTML 标记,以便在您的页面第一次加载时它不是空白的,并且搜索引擎机器人可以为 SEO(SSR 的一个用例)索引它。因此 hydra 将 JS 添加到您的页面或应用 SSR 的节点中。以便您的页面响应用户执行的事件。

渲染用于在客户端浏览器上渲染组件另外,如果您尝试用渲染替换水合物,您将收到一个警告,即渲染已被弃用,并且在 SSR 的情况下无法使用。它被删除是因为它比水合物慢。

于 2019-02-11T18:33:14.257 回答
30

除了以上...

ReactDOM.hydrate()与 相同render(),但它用于水合(附加事件侦听器)其 HTML 内容由 ReactDOMServer 呈现的容器。React 将尝试将事件侦听器附加到现有标记

由于速度慢,不推荐使用 ReactDOM.render() 来水合服务器渲染的容器,并将在React 17中删除,因此请hydrate()改用。

于 2018-05-14T12:35:52.477 回答
23

将功能放回已经在服务器端 React 中呈现的 HTML 的整个过程称为水合。

因此,在曾经渲染的 HTML 上重新渲染的过程称为水合。

因此,如果我们尝试通过调用ReactDOM.render()它应该通过调用来完成我们的应用程序ReactDOM.hydrate()

于 2019-04-13T01:02:34.437 回答
6

render 将清除指定元素中的任何内容(在大多数情况下称为“root”)并重建它,而 hydra 将保留指定元素中已经存在的任何内容并从中构建,从而使初始页面加载更快。

于 2020-08-08T09:34:08.947 回答
0

当我们想要在服务器端渲染你的 React 应用程序并在客户端对 JavaScript 包进行水合时使用 hydrate() ,这使我们的应用程序速度更快,还允许搜索引擎抓取你的页面以用于 SEO 目的。

但是当我们将渲染方法更改为水合物时会发生什么

在此处输入图像描述

该错误清楚地表明,如果您的应用程序不使用服务器端渲染(SSR),请使用 reactdom 渲染启动客户端渲染。 阅读这篇文章

于 2022-01-14T09:36:10.850 回答