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

扩展阅读

相关推荐
冬冬小圆帽2 小时前
防止手机验证码被刷:React + TypeScript 与 Node.js + Express 的全面防御策略
前端·后端·react.js·typescript
潜龙在渊灬9 小时前
杂谈:前端 UI 框架和 UI 组件库的区别
javascript·vue.js·react.js
前端咸鱼陈10 小时前
React首屏优化实战:从10秒到2秒!极致性能提升方案揭秘(附Webpack5/SSR/代码分割)🔥
前端·react.js
程序员小续10 小时前
React 18 并发更新的工作原理与实战应用
前端·react.js·面试
500佰11 小时前
Cursor13个神功能 | AI代码补全质量远超预期
react.js·程序员
不能只会打代码12 小时前
六十天前端强化训练之第二十天React Router 基础详解
前端·react.js·前端框架·react router 基础
鱼樱前端1 天前
从基础到深入的AST(Abstract Syntax Tree,抽象语法树)解析,结合前端框架(Vue/React)及实际应用场景的技术详解
javascript·vue.js·react.js
Cirrod1 天前
react加antd封装表格单、多选组件,支持跨页选择缓存
javascript·react.js·缓存
爱看书的小沐1 天前
【小沐学Web3D】three.js 加载三维模型(React)
javascript·react.js·vue·webgl·three.js·opengl·web3d