警告:React试图在容器中重用标记,但校验和无效

我试图得到一个同构的Node.js,Express,Webpack,React应用程序工作。 我收到以下错误。 有关如何解决它的任何build议?

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server: (client) rgin:0;display:flex;-webkit-align-items: (server) rgin:0;display:flex;align-items:center;j warning @ warning.js:45 ReactMount._mountImageIntoNode @ ReactMount.js:807 wrapper @ ReactPerf.js:66 mountComponentIntoNode @ ReactMount.js:268 Mixin.perform @ Transaction.js:136 batchedMountComponentIntoNode @ ReactMount.js:282 Mixin.perform @ Transaction.js:136 ReactDefaultBatchingStrategy.batchedUpdates @ ReactDefaultBatchingStrategy.js:62 batchedUpdates @ ReactUpdates.js:94 ReactMount._renderNewRootComponent @ ReactMount.js:476 wrapper @ ReactPerf.js:66 ReactMount._renderSubtreeIntoContainer @ ReactMount.js:550 ReactMount.render @ ReactMount.js:570 wrapper @ ReactPerf.js:66 (anonymous function) @ client.jsx:14 (anonymous function) @ iso.js:120 each @ iso.js:21 bootstrap @ iso.js:111 (anonymous function) @ client.jsx:12 __webpack_require__ @ bootstrap d56606d95d659f2e05dc:19 (anonymous function) @ bootstrap d56606d95d659f2e05dc:39 (anonymous function) @ bootstrap d56606d95d659f2e05dc:39 

最初服务器向浏览器传递的是这个内容:

 <!doctype html> <html lang=""> <head> <title>my title</title> <meta name="apple-mobile-web-app-title" content="my title" data-react-helmet="true" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" data-react-helmet="true" /> <meta name="apple-mobile-web-app-capable" content="yes" data-react-helmet="true" /> <meta name="mobile-web-app-capable" content="yes" data-react-helmet="true" /> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" data-react-helmet="true" /> <meta name="description" content="my description." data-react-helmet="true" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" data-react-helmet="true" /> <meta charset="utf-8" data-react-helmet="true" /> <link rel="stylesheet" href="/assets/styles/reset.css" data-react-helmet="true" /> <link rel="stylesheet" href="/assets/styles/base.css" data-react-helmet="true" /> <link rel="stylesheet" href="/assets/styles/Carousel.css" data-react-helmet="true" /> <link rel="stylesheet" href="/assets/styles/main.css" data-react-helmet="true" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Condensed" type="text/css" data-react-helmet="true" /> <link rel="icon" href="/assets/185bb6f691241307862b331970a6bff1.ico" type="image/x-icon" data-react-helmet="true" /> SCRIPT </head> <body> <script src="https://cdn.firebase.com/js/client/2.2.7/firebase.js"></script> <script src="https://cdn.firebase.com/libs/reactfire/0.4.0/reactfire.min.js"></script> <div class="app"> <div class="___iso-html___" data-key="_0"><div data-reactid=".1hkqsbm9n9c" data-react-checksum="794698749"><div data-reactid=".1hkqsbm9n9c.0"><div data-reactid=".1hkqsbm9n9c.0.$=10"></div><div style="position:fixed;z-index:2;top:0;left:0;right:0;height:60px;color:rgb(219,219,219);font-family:mainnextcondensed_ultralight;font-size:17px;overflow:hidden;" data-reactid=".1hkqsbm9n9c.0.$/=11"><div style="position:absolute;left:0;top:0;background-color:rgba(27,27,27,0.92);padding-right:35px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10"><div style="float:left;height:60px;width:13px;border-left:5px solid rgb(210,45,164);" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=10"></div><div style="float:left;height:60px;width:227px;background-image:url(data:image/png;base64,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);background-repeat:no-repeat;background-position:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=11"></div><div style="display:none;width:0;height:0;border-style:solid;border-width:6px 6px 0 6px;border-color:rgb(117,117,117) transparent transparent transparent;-webkit-transform:rotate(360deg);float:left;margin-left:6px;margin-top:26px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=10.$/=12"></div></div><div style="position:absolute;top:0px;left:280px;width:340px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11"><div style="background-color:rgba(27,27,27,0.92);height:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=10"></div><div style="background-color:rgba(53,53,53,0.84);height:40px;position:relative;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11"><div style="position:absolute;top:0;bottom:0;left:0;right:0;padding:0;margin:0;display:flex;align-items:center;justify-content:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$=10"><div style="background-image:url('/assets/3bec3e57cb5ee05658440d21984fb7b7.png');background-repeat:no-repeat;background-position:-58px -194px;width:23px;height:22px;position:absolute;top:50%;left:10px;margin-top:-11px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$=10.$icon"></div></div><div style="position:absolute;left:40px;right:40px;top:0px;bottom:0px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$/=12"><input type="text" style="width:100%;height:100%;font-size:14px;font-family:mainnext_regular;background-color:transparent;color:#ffffff;" placeholder="SEARCH ARTISTS, TRACKS, ALBUMS" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=11.$/=12.0"/></div></div><div style="background-color:rgba(27,27,27,0.92);height:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=11.$/=12"></div></div><div style="position:absolute;top:0px;left:620px;right:0px;background-color:rgba(27,27,27,0.92);height:60px;line-height:60px;overflow:hidden;min-width:500px;padding-left:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12"><div style="position:absolute;top:0px;bottom:0px;right:0px;width:357px;padding-left:141px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0"><a class="" href="/import" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10"><div style="padding-left:40px;position:absolute;left:0px;top:10px;bottom:10px;cursor:pointer;line-height:40px;color:rgb(255,255,255);" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import"><div style="position:absolute;top:0;bottom:0;left:0;right:0;padding:0;margin:0;display:flex;align-items:center;justify-content:center;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.$=10"><div style="background-image:url('/assets/3bec3e57cb5ee05658440d21984fb7b7.png');background-repeat:no-repeat;background-position:0px -194px;width:28px;height:28px;position:absolute;top:50%;left:0px;margin-top:-14px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.$=10.$icon"></div></div><span data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$/=10.$import.1">Import Playlists</span></div></a><div style="margin-left:10px;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$admin/=1$admin"><div style="cursor:pointer;float:left;" data-reactid=".1hkqsbm9n9c.0.$/=11.$/=12.0.$admin/=1$admin.$login">Login</div></div></div></div></div></div><noscript data-reactid=".1hkqsbm9n9c.1"></noscript></div></div> <div class="___iso-state___" data-key="_0" data-meta="{}" data-state="&quot;{\&quot;UserStore\&quot;:{\&quot;user\&quot;:{\&quot;authenticated\&quot;:false,\&quot;isWaiting\&quot;:false}},\&quot;SearchStore\&quot;:{\&quot;focused\&quot;:false,\&quot;input\&quot;:\&quot;\&quot;,\&quot;timeout\&quot;:null,\&quot;searchRequests\&quot;:[],\&quot;artists\&quot;:null,\&quot;artistsFailed\&quot;:false,\&quot;artistsLoading\&quot;:false,\&quot;tracks\&quot;:null,\&quot;tracksFailed\&quot;:false,\&quot;tracksLoading\&quot;:false,\&quot;albums\&quot;:null,\&quot;albumsFailed\&quot;:false,\&quot;albumsLoading\&quot;:false,\&quot;playlists\&quot;:null,\&quot;playlistsFailed\&quot;:false,\&quot;playlistsLoading\&quot;:false,\&quot;youtubes\&quot;:null,\&quot;youtubesFailed\&quot;:false,\&quot;youtubesLoading\&quot;:false,\&quot;soundclouds\&quot;:null,\&quot;soundcloudsFailed\&quot;:false,\&quot;soundcloudsLoading\&quot;:false},\&quot;PlayerStore\&quot;:{\&quot;player\&quot;:null,\&quot;playerSecond\&quot;:null,\&quot;playingTrack\&quot;:null,\&quot;playingTrackSecond\&quot;:null,\&quot;videoId\&quot;:null,\&quot;videoIdSecond\&quot;:null,\&quot;makingPlayingTrackPlayable\&quot;:false,\&quot;radio\&quot;:false,\&quot;startSeconds\&quot;:0,\&quot;current\&quot;:0,\&quot;total\&quot;:0,\&quot;perc\&quot;:0,\&quot;currentSecond\&quot;:0,\&quot;totalSecond\&quot;:0,\&quot;percSecond\&quot;:0,\&quot;playing\&quot;:false,\&quot;playingSecond\&quot;:false,\&quot;secondsListened\&quot;:0,\&quot;secondsListenedSecond\&quot;:0,\&quot;expand\&quot;:false,\&quot;source\&quot;:null,\&quot;tracksQueue\&quot;:[],\&quot;tracksPrevQueue\&quot;:[],\&quot;favorite\&quot;:false,\&quot;random\&quot;:false,\&quot;repeat\&quot;:false,\&quot;mute\&quot;:false,\&quot;volume\&quot;:100,\&quot;mode\&quot;:\&quot;standard\&quot;},\&quot;ImportStore\&quot;:{\&quot;url\&quot;:\&quot;\&quot;,\&quot;error\&quot;:false,\&quot;focused\&quot;:false,\&quot;loading\&quot;:false,\&quot;loaded\&quot;:false,\&quot;playlist\&quot;:null}}&quot;"></div> </div> <!-- Google Analytics: change UA-XXXXX-X to be your site's ID --> <!-- <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-XXXXX-X', 'auto'); ga('send', 'pageview'); </script> --> <script src="https://cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.3/fastclick.min.js"></script> <script type="text/javascript"> if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false); } </script> <script type="text/javascript" charset="utf-8" src="/assets/app.js"></script> </body> </html> 

这是我的server.jsx:

 import Iso from 'iso'; import React from 'react'; import ReactDomServer from 'react-dom/server'; import { RoutingContext, match } from 'react-router' import createLocation from 'history/lib/createLocation'; import alt from 'altInstance'; import routes from 'routes.jsx'; import html from 'base.html'; /* * @param {AltObject} an instance of the Alt object * @param {ReactObject} routes specified in react-router * @param {Object} Data to bootstrap our altStores with * @param {Object} req passed from Express/Koa server */ const renderToMarkup = (alt, state, req, res) => { let markup, content; let location = new createLocation(req.url); alt.bootstrap(state); match({ routes, location }, (error, redirectLocation, renderProps) => { if (redirectLocation) res.redirect(301, redirectLocation.pathname + redirectLocation.search) else if (error) res.status(500).send(error.message) else if (renderProps == null) res.status(404).send('Not found') else content = ReactDomServer.renderToString(<RoutingContext {...renderProps} />); markup = Iso.render(content, alt.flush()); }); return markup; }; /* * Export render function to be used in server/config/routes.js * We grab the state passed in from the server and the req object from Express/Koa * and pass it into the Router.run function. */ export default function render(state, req, res) { const markup = renderToMarkup(alt, state, req, res); return html.replace('CONTENT', markup); }; 

这是我的client.jsx:

 import React from 'react'; import ReactDOM from 'react-dom'; import Iso from 'iso'; import createBrowserHistory from 'history/lib/createBrowserHistory'; import { Router } from 'react-router'; import alt from 'altInstance'; import routes from 'routes.jsx'; /* * Client side bootstrap with iso and alt */ Iso.bootstrap((state, _, container) => { alt.bootstrap(state); ReactDOM.render(<Router history={createBrowserHistory()} children={routes} />, container); }); 

和我的routes.jsx:

 import React from 'react'; import Route from 'react-router'; import App from 'components/App'; import ImportPlaylist from 'components/ImportPlaylist'; import Login from 'components/Login'; import Logout from 'components/Logout'; import Player from 'components/Player/Player'; import Test from 'components/Test'; export default ( <Route path="/" component={App}> <Route path="login" component={Login} /> <Route path="logout" component={Logout} /> <Route name="test" path="test" component={Test} /> <Route name="import" path="import" component={ImportPlaylist} /> <Route name="player" path="/:playlist" component={Player} /> </Route> ); 

注意:这适用于旧版本的React。 如果你使用React 16,你应该使用ReactDOM.hydrate()

此外 ,下面的答案之一会提示下面的build议会导致客户端重新渲染。


这听起来很疯狂,但是在你的服务器端模板中,把你的React标记包装在一个额外的<div>

 <!-- hypothetical handlebars template --> <section role="main" class="react-container"><div>{{{reactMarkup}}}</div></section> 

为什么这个工作? 在客户端,React倾向于用一个多余的div来渲染你的根组件。 ReactDOMServer.render似乎没有这种行为,因此当同态渲染到同一个容器中时,DOM的Adler-32校验和会有所不同。

对于那些使用Google进行search的人来说,结束这个问题的一个奇怪的方法就是你甚至不使用同构渲染(即不在服务器端渲染任何东西)。 当使用带有HtmlWebpackPlugin的模板来处理index.html文件时,这发生在我身上。

在我的index.html文件中,我自己已经包含了bundle.js文件,上面的插件还通过脚本src包含了另一个bundle.js 。 确保你设置inject: false到你的HtmlWebpackPlugin构造函数。

对于我完全查杀nodejs并重新开始工作

警告这里stream行的答案是不正确的。 它正在做的是完全删除现有的DOM,并用客户端上的新鲜渲染replace它。 这意味着你失去了来自React的快速浅层渲染,并且正在浪费perf,结果也吞噬了OP错误以及其他可能的错误。

你的问题似乎与CSS – 如果你正在使用autoprefixer和内联样式,这将解释你的区别。

服务器端已经呈现了align-items:center,客户端已经意识到它在webkit浏览器中,并自动为你添加前缀到-webkit-align-items。

请发布有关CSS设置的更多信息,以及使用内联样式或类似内容的任何组件。

如果要在布局组件中呈现主要内容,则需要将布局呈现为静态标记(无反应属性),以便内容的校验和在客户端和服务器之间匹配。

服务器:

 app.get('/', (req, res) => { // render the content to a string so it has the same checksum as on the client // render the layout to static markup so that it does affect the checksum // this ensures that rendering is isomorphic and client doesn't override server markup const content = reactDomServer.renderToString(<MyContent />) const html = '<!DOCTYPE html>' + reactDomServer.renderToStaticMarkup(<HtmlLayout content={content} />) res.send(html) }) 

HtmlLayout:

 export default class HtmlLayout extends React.Component<any, any> { public render () { return ( <html lang='en'> <head> <meta charSet='utf-8' /> <meta name='viewport' content='width=device-width, initial-scale=1' /> <title>Untitled</title> <link rel='stylesheet' href='/style/bundle.css' /> </head> <body> { /* insert the content as a string so that it can be rendered separate with its own checksum for proper server-side rendering */ } <div id='content' dangerouslySetInnerHTML={ {__html: this.props.content} } /> <script src='scripts/bundle.js'></script> </body> </html> ) } } 

客户:

 const root = document.getElementById('content') DOM.render(<MyContent />, root) 

参考: http : //jeffhandley.github.io/QuickReactions/20-final-cleanup

在我的情况下,这个问题是由于我使用的是'react-responsive'中的MediaQuery组件而不传递组件使用的'value'属性,当它无法访问屏幕宽度(例如在服务器上)。

我在一个我正在研究的同构应用程序中遇到了这个问题。 什么对我来说是信不信由你,清空caching和硬重新加载在Chrome上的应用程序。 看起来像旧的DOM以某种方式caching在浏览器:)