我已经阅读了文档,但我并不真正理解 React 16 中 hydrate()
和 render()
的区别。
我知道 hydrate()
用于结合服务器端渲染和客户端渲染。
有人能解释一下什么是hydration,并且ReactDOM中的区别是什么吗?
我已经阅读了文档,但我并不真正理解 React 16 中 hydrate()
和 render()
的区别。
我知道 hydrate()
用于结合服务器端渲染和客户端渲染。
有人能解释一下什么是hydration,并且ReactDOM中的区别是什么吗?
根据 ReactDOMServer 文档(重点是我的):
如果你在一个已经拥有这个服务端渲染标记的节点上调用
ReactDOM.hydrate()
,React将保留它并仅附加事件处理程序,从而使您拥有非常高效的首次加载体验。
粗体文本是主要区别。render
可能会更改您的节点,如果初始DOM和当前DOM之间存在差异。而hydrate
只会附加事件处理程序。
根据Github问题引入hydrate
作为单独API的说明:
如果这是您的初始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 树中。
class="spinner"
属性保留在 <div>
元素中的原因。 - Glenn Mohammad关于使用hydrate
的具体内容,我没有什么具体的补充,但是在尝试学习它时,我编写了一个小例子,所以这里是为那些发现它有帮助的人的工作。
提供两个页面,一个使用ReactDOM.hydrate
,另一个使用ReactDOM.render
。它们将依赖一些用JSX编写的React组件,这些组件通过<script>
标签加载,并由服务器进行人为延迟,以说明hydrate
和render
之间的差异。
生成页面并运行服务器后,我访问127.0.0.1
,看到一个标题为hydrate的页面,一个按钮和两个链接。我可以点击按钮,但是没有任何反应。过了几秒钟,文档完成加载后,按钮开始计数我的点击次数。然后我点击"render"链接。现在,我看到的页面有一个标题为render和两个链接,但是没有按钮。几秒钟后,按钮出现并立即响应。
在"hydrate"页面上,因为所有必要的HTML都随页面一起提供,所以所有标记都会立即呈现。按钮无响应,因为尚未连接任何回调函数。一旦components.js
加载完成,window
就会触发load
事件,并使用hydrate
连接回调。
在"render"页面上,按钮标记不随页面一起提供,而是仅由ReactDOM.render
注入,因此它不会立即显示。请注意,脚本最终加载后页面的外观被突然更改。
这是我正在使用的自定义React组件。它将由node中的React静态渲染组件使用,也将从服务器动态加载以用于页面(这是在文件开头检查exports
和React
对象的目的)。
// 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');
在 SSR(服务器端渲染)情况下,Hydrate 功能主要用于将 JS 添加至页面或应用 SSR 的节点。SSR 会提供骨架或 HTML 标记,这些标记从服务器发送,以便在页面首次加载时不会是空白的,并使搜索引擎机器人可以进行 SEO 索引(SSR 的一种用例)。因此,使用 Hydrate 可实现响应用户执行的事件。
Render 主要用于在客户端浏览器上渲染组件。如果您尝试使用 Render 替换 Hydrate,则会收到警告,说明 Render 已不建议在 SSR 情况下使用,这是因为相对于 Hydrate,它速度较慢而被删除了。
除了以上内容...
ReactDOM.hydrate()
与 render()
相同,但它用于对由ReactDOMServer呈现的HTML内容进行水合作用(即附加事件侦听器)。React将尝试将事件侦听器附加到现有标记。
使用ReactDOM.render()来水合化服务器呈现的容器已经被弃用,因为速度较慢,并将在React 17中删除,所以请改用hydrate()
。
ReactDOM.render()
来实现应用程序的水合作用,则应该使用调用 ReactDOM.hydrate()
。render会清空指定元素(在大多数情况下命名为“root”)中的所有内容并重新构建它,而hydrate将保留已经存在于指定元素中的所有内容并从中构建,从而使初始页面加载更快。