我已经阅读了文档,但是我并没有真正理解React 16hydrate()
和之间的区别render()
。
我知道hydrate()
用来结合SSR和客户端渲染。
有人可以解释一下什么是补水,然后ReactDOM有什么区别?
Answers:
从ReactDOMServer文档(重点是我的):
如果您调用
ReactDOM.hydrate()
已经具有此服务器渲染标记的节点,React将保留它并仅附加事件处理程序,从而使您获得非常出色的首次加载体验。
粗体文本是主要区别。render
如果初始DOM与当前DOM之间存在差异,则可能会更改您的节点。hydrate
将仅附加事件处理程序。
从作为单独的API引入hydrate
的Github问题开始:
如果这是您的初始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>
元素中的原因。
关于的使用,我没有什么特别的要补充的内容hydrate
,但是在尝试学习它的过程中,我举了一个小例子,因此,对于那些发现它有用的人来说,这里的工作都是如此。
服务两页,一页使用ReactDOM.hydrate
,另一页使用ReactDOM.render
。他们将取决于一些反应写在JSX组件,这些组件通过加载<script>
,因为人为延迟(服务器)来说明之间的区别标签hydrate
和render
。
生成页面并运行服务器后,我转到127.0.0.1
并看到标题hydrate,一个按钮和两个链接。我可以单击该按钮,但是什么也没有发生。片刻之后,文档完成加载,并且按钮开始计算我的点击次数。然后,我单击“渲染”链接。现在,向我展示的页面具有标题渲染和两个链接,但是没有按钮。片刻之后,该按钮出现并立即响应。
在“水合物”页面上,所有标记都会立即呈现,因为所有必需的html都随该页面一起提供。该按钮没有响应,因为还没有连接回调。一旦components.js
完成加载,将load
事件从火灾window
和回调与连接hydrate
。
在“渲染”页面上,按钮标记不随页面一起提供,而仅由注入ReactDOM.render
,因此不会立即显示。请注意,最终加载的脚本如何显着改变页面的外观。
这是我正在使用的自定义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将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');