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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46516395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:29:01  来源:igfitidea点击:

What's the difference between hydrate() and render() in React 16?

javascriptreactjsreact-domreact-fiber

提问by shabenda

I've read the documentation, but I didn't really understand the difference between hydrate()and render()in React 16.

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

I know hydrate()is used to combine SSR and client-side rendering.

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

Can someone explain what is hydrating and then what is the difference in ReactDOM?

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

回答by topher

From the ReactDOMServerdocs (emphasis mine):

来自ReactDOMServer文档(重点是我的):

If you call ReactDOM.hydrate()on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.

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

The text in bold is the main difference. rendermay change your node if there is a difference between the initial DOM and the current DOM. hydratewill only attach event handlers.

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

From the Github issue that introduced hydrateas a separate API:

作为单独 API引入hydrateGithub 问题

If this is your initial DOM:

如果这是您的初始 DOM:

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

and then call:

然后调用:

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

intending to do a client-side only render (not hydration). Then you end with

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

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

Because we don't patch up the attributes.

因为我们没有修补属性。

Just FYI the reason they didn't patch the attributes is

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

... This would be really slow to hydrate in the normal hydration mode and slow down initial render into a non-SSR tree.

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

回答by Devinder Suthwal

In addition to above...

除了以上...

ReactDOM.hydrate()is same as render(), but it is used to hydrate(attach event listeners) a containerwhose HTML contents were rendered by ReactDOMServer. React will attempt to attach event listeners to the existing markup.

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

Using ReactDOM.render() to hydrate a server-rendered container is deprecated because of slowness and will be removed in React 17so use hydrate()instead.

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

回答by Sumit Kapoor

Hydrate is basically used in case of SSR(Server side Rendering). SSR gives you the skeleton or HTML markup which is being shipped from a server so that for the first time when your page loads it is not blank and search engine bots can index it for SEO(A use case of SSR). So hydrate adds the JS to your page or a node to which SSR is applied. So that your page responds to the events performed by the user.

Hydrate 基本上用于 SSR(服务器端渲染)的情况。SSR 为您提供从服务器发送的骨架或 HTML 标记,以便您的页面第一次加载时它不是空白的,搜索引擎机器人可以将其索引以用于 SEO(SSR 的一个用例)。所以 hydr 将 JS 添加到您的页面或应用 SSR 的节点。以便您的页面响应用户执行的事件。

Render is used for rendering the component on client side browser Plus if you try to replace the hydrate with render you will get a warning that render is deprecated and can't be used in case of SSR. it was removed because of it being slow as compared to hydrate.

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

回答by Daniel

The entire process of putting functionality back into the HTML that was already rendered in server side React is called hydration.

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

So the process of re-rendering over the once rendered HTML is referred to as hydration.

因此,重新渲染曾经渲染过的 HTML 的过程称为 hydration。

So if we try to hydrate our application by calling ReactDOM.render()its supposed to be done by calling ReactDOM.hydrate().

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

回答by Nathan Chappell

I don't have anything specific to add to what's been said above about the use of hydrate, but in trying to learn about it I put together a little example, so here's the work for whoever finds it helpful.

我没有任何具体的内容可以添加到上面关于 的使用的内容中hydrate,但是在尝试了解它的过程中,我整理了一个小示例,因此这里的工作适合任何认为有帮助的人。

Goal

目标

Serve two pages, one which uses ReactDOM.hydrateand one which uses ReactDOM.render. They will depend upon some react components written in JSX, which are loaded by <script>tags, given artificial delay (by the server) to illustrate the difference between hydrateand render.

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

Basic Structure

基本结构

  1. One file which has the HTML "skeleton"
  2. One file with the custom React components written in JSX
  3. One script which generates all pages for the server to use
  4. One script to run the server
  1. 一个包含 HTML“骨架”的文件
  2. 一个包含用 JSX 编写的自定义 React 组件的文件
  3. 一个脚本生成所有页面供服务器使用
  4. 一个脚本来运行服务器

Results

结果

After I generate the pages and run the server, I go to 127.0.0.1and am presented with the header hydrate, a button, and two links. I can click the button, but nothing happens. After a few moments, the document finishes loading and the button starts counting my clicks. Then I click on the "render" link. Now, the page I'm presented with has the header renderand two links, but no button. After a few moments, the button appears and is immediately responsive.

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

Explanation

解释

On the "hydrate" page, all the markup is immediately rendered, because all the necessary html is served with the page. The button is unresponsive because there are no callbacks connected yet. Once components.jsfinishes loading, the loadevent fires from the windowand the callbacks are connected with hydrate.

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

On the "render" page, the button markup isn't served with the page, but only injected by ReactDOM.render, so it isn't immediately visible. Note how the appearance of the page is jarringly changed by the script finally loading.

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

Source

来源

Here is the custom react component I am using. It will be used by the server in node with react to statically render components, and it will also be loaded dynamically from the server for use in pages (this is the purpose of checking for exportsand Reactobjects at the beginning of the file).

这是我正在使用的自定义反应组件。它将由在节点的服务器可以使用具有反应以静态呈现组件,并且它也将被从服务器中的网页使用动态加载(这是检查的目的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;

This is the script used to generate all the pages required for the server. First, babel is used to transpile components.jsx into javascript, then these components are used, along with React and ReactDOMServer, to create the actual pages. These pages are created with the fuction getPagewhich is exported from the file pageTemplate.js, shown next.

这是用于生成服务器所需的所有页面的脚本。首先,使用 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);

This file just exports the getPagefunction mentioned previously.

这个文件只是导出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>
  `;
}

Finally, the actual server

最后,实际服务器

// 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');