在React 16中hydrate()和render()有什么区别?


Answers:


75

ReactDOMServer文档(重点是我的):

如果您调用ReactDOM.hydrate()已经具有此服务器渲染标记的节点,React将保留它并仅附加事件处理程序,从而使您获得非常出色的首次加载体验。

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

作为单独的API引入hydrateGithub问题开始

如果这是您的初始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树中的速度。


7
我不明白为什么渲染的div没有类名myapp的div,以及为什么最终的渲染元素中存在旋转器类
pravin poudel

1
@pravinpoudel我认为这是因为它们在客户端渲染期间不会修补属性。这就是为什么属性class="spinner"保留在<div>元素中的原因。
Glenn Mohammad

26

水合物基本上用于SSR(服务器端渲染)。SSR为您提供了从服务器附带的框架或HTML标记,因此,在页面首次加载时,它不会为空白,并且搜索引擎机器人可以将其索引为SEO(SSR的一个用例)。因此,水合物会将JS添加到您的页面或要应用SSR的节点。这样您的页面才能响应用户执行的事件。

渲染用于在客户端浏览器Plus上渲染组件,如果尝试将水合物替换为渲染,则会收到警告,提示渲染已被弃用,在SSR情况下无法使用。由于它比水合物慢,因此将其除去。


21

除了以上...

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

使用ReactDOM.render()以水合服务器渲染容器,因为缓慢的使用,将在被移除阵营17这样使用hydrate()来代替。


19

关于的使用,我没有什么特别的要补充的内容hydrate,但是在尝试学习它的过程中,我举了一个小例子,因此,对于那些发现它有用的人来说,这里的工作都是如此。

目标

服务两页,一页使用ReactDOM.hydrate,另一页使用ReactDOM.render。他们将取决于一些反应写在JSX组件,这些组件通过加载<script>,因为人为延迟(服务器)来说明之间的区别标签hydraterender

基本结构

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

结果

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

说明

在“水合物”页面上,所有标记都会立即呈现,因为所有必需的html都随该页面一起提供。该按钮没有响应,因为还没有连接回调。一旦components.js完成加载,将load事件从火灾window和回调与连接hydrate

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

资源

这是我正在使用的自定义react组件。服务器将在节点中使用它,以对静态渲染组件做出反应,还将从服务器动态加载它以供页面使用(这是在文件开头检查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将component.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');

2
哇。您基本上回答了问题+解释了如何构建像Gatsby这样的最小静态生成器。惊人。非常感谢!
谢尔盖·卢金

17

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

因此,对曾经渲染过的HTML进行重新渲染的过程称为水合。

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


0

render将清除指定元素中的所有内容(大多数情况下称为“ root”)并进行重建,而hydr将保留指定元素中已经存在的所有内容并以此为基础进行构建,从而使初始页面加载速度更快。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.