React 服务端渲染后,客户端再次渲染,div 错位

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 时出现错位或混在一起渲染的问题。

扩展阅读

相关推荐
然我8 分钟前
React 开发通关指南:用 HTML 的思维写 JS🚀🚀
前端·react.js·html
马特说3 小时前
React金融数据分析应用性能优化实战:借助AI辅助解决18万数据量栈溢出Bug
react.js·金融·数据分析
归于尽3 小时前
useEffect玩转React Hooks生命周期
前端·react.js
G等你下课3 小时前
React useEffect 详解与运用
前端·react.js
中微子4 小时前
React Props 传值规范详解
前端·react.js
卸任4 小时前
性能优化大作战:React.memo 在可编辑列表中的奇效
前端·javascript·react.js
LeeAt4 小时前
React Hooks 编程:useState和useEffect的详解
前端·react.js
Dream耀4 小时前
React Hooks 指南:useState 与 useEffect 的用法与技巧
前端·javascript·react.js
我想说一句4 小时前
React Hooks 生存指南:让你的函数组件"活"起来 🧬
前端·javascript·react.js
LeeAt4 小时前
AI单词拍照识别移动端项目(一)
前端·react.js·ai编程