React 16.2.0 SSR水合后页面闪烁,如何调试HTML不匹配问题?
我之前也碰到过一模一样的问题,React 16.x的SSR水合警告确实有点让人头大,不过用下面这些方法能快速定位到问题:
用React DevTools锁定闪烁元素
打开浏览器的React DevTools扩展,找到顶部的"Highlight Updates"按钮打开它。页面水合完成后,那些被React重新渲染的元素会被高亮,那个神秘消失的div大概率就在高亮区域里,一眼就能锁定位置。直接对比原始HTML和客户端DOM
别用Elements面板看客户端DOM!先右键页面选"查看页面源代码",这是服务端直接输出的原始HTML,找到对应的父div,记下里面的结构。然后再切到Elements面板,对比同一个父div下的内容,就能看到哪个div在客户端被删掉了。揪出条件渲染的不一致
这种问题十有八九是服务端和客户端的条件判断不一样导致的:- 比如服务端渲染时某个状态是
true,渲染了那个div,但客户端初始化时状态是false,水合时就把它移除了; - 检查组件里有没有用到
window、document这类服务端没有的对象——服务端渲染时这些都是undefined,客户端却有,直接用它们做判断肯定会出问题; - 还要看看
process.env变量,确保服务端和客户端的环境变量(比如是否是开发环境)完全一致,不然条件分支会走不同的路。
- 比如服务端渲染时某个状态是
手动打印HTML对比找差异
默认警告信息太简洁?那就在客户端代码里手动对比服务端和客户端渲染的HTML:import ReactDOM from 'react-dom'; import { renderToString } from 'react-dom/server'; import App from './App'; // 先获取服务端渲染的原始HTML const rootElement = document.getElementById('root'); const serverRenderedHtml = rootElement.innerHTML; // 用客户端的逻辑渲染一遍HTML const clientRenderedHtml = renderToString(<App />); // 对比两者,不一样就打日志 if (serverRenderedHtml !== clientRenderedHtml) { console.log('服务端输出的HTML:', serverRenderedHtml); console.log('客户端渲染的HTML:', clientRenderedHtml); // 复制这两段到文本对比工具里,差异点一目了然 } // 正常执行水合 ReactDOM.hydrate(<App />, rootElement);控制台里的两段HTML一对比,那个消失的div马上就能找出来。
检查生命周期的坑
服务端渲染只跑constructor、static getDerivedStateFromProps和render,componentDidMount是客户端水合后才执行的。如果你的组件在componentDidMount里修改了状态,导致DOM结构变了,就会触发这个警告。所以要确保组件的初始状态在服务端和客户端完全一致,别在componentDidMount里做会改变初始DOM的操作。
内容的提问来源于stack exchange,提问作者Uchit Kumar

