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

扩展阅读

相关推荐
学渣y2 小时前
React状态管理-对state进行保留和重置
javascript·react.js·ecmascript
进取星辰3 小时前
25、Tailwind:魔法速记术——React 19 样式新思路
前端·react.js·前端框架
Bl_a_ck5 小时前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架
寧笙(Lycode)6 小时前
React系列——HOC高阶组件的封装与使用
前端·react.js·前端框架
赵大仁9 小时前
React vs Vue:点击外部事件处理的对比与实现
javascript·vue.js·react.js
Zero10171310 小时前
【React的useMemo钩子详解】
前端·react.js·前端框架
郝开1 天前
扩展:React 项目执行 yarn eject 后的 scripts 目录结构详解
react.js·前端框架·react
weifont1 天前
React中的useSyncExternalStore使用
前端·javascript·react.js
初遇你时动了情1 天前
js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
前端·javascript·react.js
几何心凉1 天前
如何使用 React Hooks 替代类组件的生命周期方法?
前端·javascript·react.js