React 16
,server render 的时候遇见一个问题,
- 页面内容:div1,div2,div3 三个从上往下
- 服务端渲染逻辑,div2,div3 (div1 判断是服务端时,返回空 null)
- 网页端渲染逻辑,div1,div2,div3 (div1 判断是网页端时,返回正常 html)
实际上看到的,
- div1 的内容 + div2 的样式 混合在一起,
- div2 的内容 + div3 的样式,混合在一起
- div3 的内容 + div3 的样式,又出现了一次
问了一下 AI,就是服务端和客户端渲染的逻辑不一致,这个在 react 中,有个专业术语:hydrate 水合
当使用 ReactDOM.hydrate
时,如果服务端和客户端的渲染结果不一致,可能会导致一些意外的行为。例如,如果服务端根据某些条件渲染了一个 div
,而在客户端这些条件不满足,导致这个 div
不存在,那么客户端在激活时可能会出现错位或混在一起渲染的问题。
为了解决这个问题,可以采取以下几种方法:
1. 确保 服务端和客户端 条件一致
确保服务端和客户端的渲染条件完全一致。这意味着在服务端和客户端使用相同的逻辑和状态来决定是否渲染某个组件。
javascript
// 服务端和客户端共享的逻辑
const shouldRenderDiv = checkSomeCondition();
const App = () => (
<div>
{shouldRenderDiv && <div>Conditional Div</div>}
<div>Another Div</div>
</div>
);
2. 使用占位符
如果某个组件可能被渲染为 null
,可以在服务端和客户端都渲染一个占位符元素,以确保结构一致。
javascript
const App = () => (
<div>
<div style={{ display: shouldRenderDiv ? 'block' : 'none' }}>
Conditional Div
</div>
<div>Another Div</div>
</div>
);
这样,即使 Conditional Div
不被渲染,占位符元素仍然存在,确保了结构的一致性。
3. 使用 key
属性
在 React 中,使用 key
属性可以帮助 React 更好地识别和管理列表中的元素。同样,在条件渲染的组件中,使用 key
属性也可以帮助 React 更好地匹配和激活组件。
javascript
const App = () => (
<div>
{shouldRenderDiv && <div key="conditional">Conditional Div</div>}
<div key="another">Another Div</div>
</div>
);
4. 检查并清理 DOM
在客户端激活之前,可以检查并清理 DOM,以确保其结构与 React 组件树一致。
javascript
const rootElement = document.getElementById('root');
// 清理 rootElement 中的多余节点
while (rootElement.firstChild) {
rootElement.removeChild(rootElement.firstChild);
}
// 然后使用 ReactDOM.hydrate
ReactDOM.hydrate(<App />, rootElement);
5. 使用 Error Boundaries
如果由于某些原因导致渲染结果不一致,可以使用 React 的 Error Boundaries 来捕获并处理错误。
javascript
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 处理错误
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
const App = () => (
<ErrorBoundary>
<div>
{shouldRenderDiv && <div>Conditional Div</div>}
<div>Another Div</div>
</div>
</ErrorBoundary>
);
通过以上方法,可以尽量确保服务端和客户端的渲染结果一致,从而避免 ReactDOM.hydrate
时出现错位或混在一起渲染的问题。