在反应16中,水合物()和渲染()的区别是什么?

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

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

谁能解释一下什么是水合作用,那么 ReactDOM 的区别是什么?

79061 次浏览

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

If you call ReactDOM.hydrate() on a node that already has this server-rendered markup, React 将保存它,并且只附加事件处理程序, allowing you to have a very performant first-load experience.

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

From the 引入 hydrate作为单独 API 的 Github 问题:

If this is your initial 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>

因为我们不修补属性。

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

... 在正常的水合模式下水合作用会非常缓慢并且会降低初始渲染到非 SSR 树上的速度。

除此之外..。

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

使用 ReactDOM.render ()对服务器呈现的容器进行水合处理是不推荐的,因为速度慢,在 反应17中将被删除,所以使用 hydrate()代替。

水合物主要用于 SSR (服务器端渲染)。SSR 提供从服务器发布的框架或 HTML 标记,这样当你的页面首次加载时,它不是空白的,搜索引擎机器人可以为 SEO (SSR 的一个用例)对它进行索引。因此,将 JS 添加到页面或应用 SSR 的节点。以便页面响应用户执行的事件。

渲染用于在客户端浏览器 Plus 上渲染组件,如果你试图用渲染代替水合物,你会得到一个警告: 渲染已被废弃,不能在 SSR 情况下使用。因为它比水合物慢,所以被移除了。

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

因此,在一次渲染的 HTML 上重新呈现的过程称为水合作用。

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

我没有任何具体的东西来补充上面所说的关于使用 hydrate的内容,但是为了了解它,我收集了一个小例子,所以这里是为那些认为它有帮助的人所做的工作。

Goal

提供两个页面,一个使用 ReactDOM.hydrate,一个使用 ReactDOM.render。它们将依赖于用 JSX 编写的一些反应组件,这些组件由 <script>标记加载,并给定人为的延迟(由服务器)来说明 hydraterender之间的区别。

基本结构

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

Results

在我生成页面并运行服务器之后,我转到 127.0.0.1,看到头 补充水分、一个按钮和两个链接。我可以点击按钮,但什么都没有发生。过了一会儿,文档加载完毕,按钮开始计算我的点击次数。然后我点击“渲染”链接。现在,我看到的页面有头 render和两个链接,但没有按钮。过了一会儿,按钮出现,并立即响应。

解释

在“水合物”页面上,所有的标记都会立即呈现出来,因为所有必要的 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.

Source

下面是我正在使用的自定义反应组件。它将被节点中的服务器使用,并对静态呈现组件做出反应,它还将从服务器动态加载,以便在页面中使用(这是在文件开头检查 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 ents.jsx 转换为 javascript,然后使用这些组件以及 React 和 ReactDOMServer 创建实际的页面。这些页面是使用从文件 pageTemplate.js导出的函数 getPage创建的,如下所示。

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

Render 将清除指定元素中的任何内容(在大多数情况下称为“ root”)并重新构建它,而水合物将保留指定元素中的任何内容并从该元素构建,从而加快初始页面加载速度。